异步可重入函数与线程安全函数等价吗?

apue的信号和多线程两章都提到了函数重入性,在异步信号下可重入的函数叫异步可重入,在多线程下可重入叫线程安全。那么这两种情况下的可重入函数是否等价,…
关注者
189
被浏览
20,930

13 个回答

在Unix/Linux系统中,signal是以软中断的方式分发的,signal handler可能在任何时候打断一个进程的任意一个线程而执行(如果该线程没有屏蔽该signal的话)。

比如,pthread_mutex_lock函数显然是线程安全的,但是它不是异步可重入的,考虑下面的情况,有这么一段代码:

pthread_mutex_lock(&gLock);

change_some_thing();

pthread_mutex_unlock(&gLock);

假定有一个工作线程A运行到这段代码,调用了pthread_mutex_lock但是还没返回的时候,有个signal产生了,signal handler打断线程A执行,然后在signal handler的上下文中也运行到了这段代码(注意signal handler其实借用了线程A的栈执行代码,这一点很像操作系统内核的中断处理),signal handler调用pthread_mutex_lock的时候,线程A的pthread_mutex_lock可能才执行了一半,因为pthread_mutex_lock不是异步可重入的,所以在signal handler的上下文中的pthread_mutex_lock调用很可能会破坏pthread_mutex_t的内部状态,导致程序死锁等异常行为。

类似的情况还有很多,最后导致的结果也不尽相同。

不过异步可重入应该是Unix/Linux这种支持signal的系统特有的。在Windows下,其实并不存在类似的问题,Windows的C Runtime虽然也有signal这样的函数,但是它更像是为了保持向前兼容而做的模拟,因为Windows的signal是通过特定的线程分发的,所以它不会打断应用的线程。所有线程安全的函数在Windows的signal handler中应该都可以使用。在Windows中,SEH机制更接近Unix/Linux中的signal handler,但是显然SEH更可控一点。

个人并不喜欢Unix/Linux中的signal机制,Unix/Linux中的signal机制更像是一种用户态的中断,当它和多线程机制同时出现的时候,总是显得格格不入。

Linux 中可重入这个概念一般只有在 signal 的场景下有意义,叫 async-signal-safe。

很多线程安全的函数都是不可重入的,例如 malloc。

可重入的函数一般也是线程安全的,虽然据说有反例,但我没见过。

Posix中大多数函数都是线程安全的,但只有少数是 async-signal-safe。

见《Linux多线程服务端编程》第4.2节和第4.10节。