可重入函数与线程安全


一个问题


为了理解可重入函数的概念,我们先来看这样一个例子:

main函数调用insert函数向链表中头插节点node1,插入操作分为两步。

1

假设它刚做完第一步的时候。由于硬件中断使进程切换到内核。再次回用户态之前检查到有信号待处理,于是切换到sighandler函数,sighandler也调用insert函数向同一个链表中头插节点node2。

2

把插入操作的两步都做完之后从sighandler返回内核态,再次回到用户态从main函数调用的insert函数中继续执⾏第⼆步。

3

结果是:main函数和sighandler先后向链表中插入两个节点,而最后只有一个节点node1插入链表中:

4

像这样,两个执行流main和sighandler同时访问insert函数,就会导致结点丢失。类似于insert这种情况的函数就叫做不可重入函数
这里的重入是指:同一时刻一个函数被多个执行流调用

相对的,有可重入函数:只访问自己私有栈的变量和参数。这个例子同样说明:没有线程,一个进程里也会有多个执行流,比如信号的捕捉。

所以,我们可以总结出可称为不可重入函数的3个条件:

1.调用了malloc或free,因为malloc也是用全局链表来管理堆的。
2.调⽤了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使⽤全局数据结构。
3.必须以线程安全的方式实现的系统函数。

从第3条我们可以看出,可重入/不可重入函数与一个概念相关,这就是线程安全


线程安全与可重入函数


首先,我们需要明确线程安全的概念:一个线程对全局变量这种具有全局性的东西的操作影响到别的线程。简单来说,只要一个程序/进程的线程之间没有涉及到对全局变量的操作,那么这个程序/进程就是线程安全的。

这样看来,线程安全和可重入函数貌似是一样的。后者也是只访问自己私有栈的变量和参数,没有访问全局变量。

其实,可重入函数与线程安全并不相同,一般来说,可重入的函数一定是线程安全的,但反过来就不一定成立了。

5

前方高能:一大段文字即将来袭。。。

总结:可重入函数与线程安全的区别与联系

1.线程安全是在多个线程情况下引发的,而可重入函数在只有一个线程的情况下也可以有。 2.线程安全不一定是可重入的,而可重入函数则一定是线程安全的。 3.如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。 4.如果在某函数中将对临界资源的访问加上锁,则这个函数是线程安全的。 5.线程安全函数能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据操作互不影响。 6.可重入函数是线程安全函数的一种,其特点在于它们被多个线程调用时,不会引用任何共享数据。