多线程之Thread-safe和Async-signal safe分析(十二)

本文探讨了异步信号安全(async-signal-safe)与线程安全(thread-safe)的概念及其区别,详细解释了如何确保函数在多线程环境及信号处理过程中能够安全执行。

1. 写在前面

笔者刚开始看到thread-safeasync-signal safe,非常草率的就过去了。笔者在开始本文前的理解:不就是重入嘛,当一个函数被调用的时候,又调用了该函数。在异步信号发生的场合对应着async-singal safe,在多线程中发生的场合对应着thread-safe 如果您的理解和笔者一样,那么本文值得您浏览一下。

2. Async-signal Safe

2.1 MAN Page

以下内容来自MAN Page

An async-signal-safe function is one that can be safely called from within a signal handler. Many functions are not async-ignal-safe. In particular, nonreentrant functions are generally unsafe to call from a signal handler.

接下来MAN Page指出,STDIO库中的函数都不是singal safe。因为其静态的分配了buffer和其他用来指示状态的变量,那么如果main中调用其中一个标准IO函数,而在该函数执行期间被信号中断,而在中断处理函数函数中又调用了该函数,必然将覆盖以前静态分配区域的数据,从而导致我们不期望的结果。对此MAN Page给出的策略就是:

  • Ensure that (a) the signal handler calls only async-signal-safe functions, and (b) the signal handler itself is reentrant with respect to global variables in the main program.
  • Block signal delivery in the main program when calling functions that are unsafe or operating on global data that is also accessed by the signal handler.

其中第二个措施并不容易实现,稍微有点复杂度的程序,信号是必须采用的手段,MAN Page给出了一个很准确的描述:

In general, a function is async-signal-safe either because it is reentrant or because it is atomic with respect to signals

NOTE: POSIX.1-2003 clarified that if an application calls fork(2) from a signal handler and any of the fork handlers registered by pthread_atfork(3) calls a function that is not async-signal-safe, the behavior is undefined. A future revision of the standard is likely to remove fork(2) from the list of async-signal-safe functions.

异步信号安全的函数即可以是可重入的,或者对于信号处理函数来说其调用是原子操作,或者两者都有。对于在信号处理函数中调用fork,此时pthread_atfork注册的三个handler中都不能调用非信号安全的函数,否则会发生无法预测的行为,笔者语:这样的限制其实蛮大的。因为无论是parperparent或者childhandler都将在signal handler的上下文执行。(signal handler是多线程及多进程共享的)

2.2 Google Site

Google Site 这篇文章比较详细的论述了这个问题,笔者从中节选出来比较合适的部分:

A function is asynchronous-safe, or asynchronous-signal safe, if it can be called safely and without side effects , without interfering other operations, from within a signal handler context. That is, it must be able to be interrupted at any point to run linearly out of sequence without causing an inconsistent state. It must also function properly when global data might itself be in an inconsistent state.

以上节选来自GUNasynchronous-safe的定义,言简意赅的说就是符合信号安全的函数,要保证在该函数执行期间的任何位置,信号处理函数中对该函数的调用都不会产生任何问题。

2.3 APUE

笔者在信号篇的总结中,总结比较不全面。APUE中使用了getpwnam做了个实验,结果程序有时候会意外终止,APUE中提到以下几个特点的函数一定不是信号安全的:

  • they are known to use static data structures
  • they call malloc or free
  • they are part of the standard I/O library

3. Thread Safe

3.1 Wikipedia
  • Thread-safe code only manipulates shared data structures in a manner that ensures that all threads behave properly and fulfil their design specifications without unintended interaction.
  • Thread safety is a property that allows code to run in multithreaded environments by re-establishing some of the correspondences between the actual flow of control and the text of the program, by means of synchronization.

第一点比较容易理解,第二点的话稍微有点绕可以这么理解:使用同步的方式来使实际的执行流和程序本身设计的内容相一致,也就是保证每个线程运行该段代码都能到达预期的目的。笔者语:线程安全,不仅仅考虑该函数可以多线程重入,也要考虑并发执行效率。维基百科中举出了几个实现线程安全的措施:

  • The first class of approaches focuses on avoiding shared state, and includes: Re-entrancy,Thread-local storage,Immutable objects
  • The second class of approaches are synchronization-related, and are used in situations where shared state cannot be avoided:Mutual exclusion, Atomic operations

接着看看维基百科中给出的一组C代码:

# include <pthread.h>

int increment_counter ()
{
 static int counter = 0;
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

 // only allow one thread to increment at a time
 pthread_mutex_lock(&mutex);

 ++counter;

 // store value before any other threads increment it further
 int result = counter;

 pthread_mutex_unlock(&mutex);

 return result;
}

以上的代码非常简单,对于多线程并发来说其一定是安全的,但是其不可重入。因为会造成死锁,所以这里笔者终于转过弯了:可重入对信号安全和线程安全的要求是不一样的,信号安全里面的可重入排除了一种使用mutex 及相关的机制,而线程安全的可重入却可以使用mutex。所以可重入的函数一定是thread safe,但是thread safe的函数并不一定是可重入的。

3.2 Man Page

A thread-safe function is one that can be safely (i.e., it will deliver the same results regardless of whether it is) called from multiple threads at the same time.

MAN Page中关于thread safe的记录只是简单的说如果允许多个thread同时调用该函数,且不会造成任何不希望的结果,则其是thread-safe

3.3 APUE

If a function is reentrant with respect to multiple threads, we say that it is thread-safe. This doesn’t tell us, however, whether the function is reentrant with respect to signal handlers. We say that a function that is safe to be reentered from an asynchronous signal handler is async-signal safe.

以上表明了一个函数重入的要求对于信号安全和线程安全来说是不同的。保证线程安全,却不一定能保证信号安全,但是如果一个信号安全的函数,其一定是线程安全的。

#include <stdio.h>
int ftrylockfile(FILE *fp);
Returns: 0 if OK, nonzero if lock can’t be acquired
void flockfile(FILE *fp);
void funlockfile(FILE *fp);

int getchar_unlocked(void);
int getc_unlocked(FILE *fp);
Both return: the next character if OK, EOF on end of file or error
int putchar_unlocked(int c);
int putc_unlocked(int c, FILE *fp);
Both return: c if OK, EOF on error

以上API其实与本篇主体关系不大,举例来说getchar_unlockedgetchar的区别仅仅在于是内部使用过了lock,不加锁版本是为了减少自带锁版本的每次处理频繁的加锁解锁的消耗。从一定意义上讲后缀有_unlocked的函数是非Thread Safe的。

4. 结论

最后,总结一下前文的内容:重入对于异步信号安全和线程安全的要求是不同的,满足异步信号安全就一定能满足线程安全。实现线程安全的方式有多种,使用原子操作,互斥锁,及使用只读变量等。但是实现信号安全的方式就有限制了,因为有可能发生死锁,所以互斥机制就不太适合。而且更为重要的是要保证信号安全的函数里面其他的函数一定都是信号安全的。

**NOTE : ** 如果您对笔者上述的论述有所疑问,请及时指正。

【轴承故障诊断】基于融合鱼鹰柯西变异的麻雀优化算法OCSSA-VMD-CNN-BILSTM轴承诊断研究【西储大学数据】(Matlab代码实现)内容概要:本文提出了一种基于融合鱼鹰柯西变异的麻雀优化算法(OCSSA)优化变分模态分解(VMD)参数,并结合卷积神经网络(CNN)与双向长短期记忆网络(BiLSTM)的轴承故障诊断模型。该方法利用西储大学公开的轴承数据集进行验证,通过OCSSA算法优化VMD的分解层数K惩罚因子α,有效提升信号分解精度,抑制模态混叠;随后利用CNN提取故障特征的空间信息,BiLSTM捕捉时间序列的动态特征,最终实现高精度的轴承故障分类。整个诊断流程充分结合了信号预处理、智能优化与深度学习的优势,显著提升了复杂工况下轴承故障诊断的准确性与鲁棒性。; 适合人群:具备一定信号处理、机器学习及MATLAB编程基础的研究生、科研人员及从事工业设备故障诊断的工程技术人员。; 使用场景及目标:①应用于旋转机械设备的智能运维与故障预警系统;②为轴承等关键部件的早期故障识别提供高精度诊断方案;③推动智能优化算法与深度学习在工业信号处理领域的融合研究。; 阅读建议:建议读者结合MATLAB代码实现,深入理解OCSSA优化机制、VMD参数选择策略以及CNN-BiLSTM网络结构的设计逻辑,通过复现实验掌握完整诊断流程,并可进一步尝试迁移至其他设备的故障诊断任务中进行验证与优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值