可重入函数和不可重入函数,这也是一种竞态条件/竞争执行。
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
int a = 1, b =1;
int sum(int *b, int *a)
{
(*a)++;
sleep(3);//sleep只是放大了这一过程,方便看清情况
(*b)++;
return *a + *b;
}
void sigcb()
{
printf(“signal------%d\n”, sum(&a, &b));
}
int main()
{
signal(SIGINT, sigcb);
printf(“main------%d\n”, sum(&a, &b));//主流程main进入到sum函数的sleep,这时ctrl+c信号到来,此时又重新进入到sum函数,这就是重入(sum函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数),也就是多个流程(因为sigcb和main是两个不同的流程)“同时”进入到一个函数。
return 0;
}
函数的可重入:多个执行流同时执行进入相同的函数,不会造成数据二义性以及代码逻辑混乱。
函数的不可重入:多个执行流同时执行进入相同的函数,有可能造成数据二义性以及代码逻辑混乱。
sum函数访问了全局变量,有可能因为重入而造成错乱,像这样的函数称为不可重入函数。
多线程本身就是多个执行流。
当用户设计/使用一个函数的时候在多个执行流中,那么这时候就需要考虑函数的是否可重入情况。如果不会出问题,就可重入;如果有问题,就不可重入。
malloc/free涉及到了全局链表的不受保护的操作,因此是一个不可重入函数。
标准I/O库的很多实现都以不可重入的方式使用全局数据结构。
函数可重入与不可重入的关键点
这个函数中是否对临界资源(在此通常都是全局数据)进行了非原子操作,也就是不受保护的操作。
原子操作是不会被打断的,谁都打断不了,一次性就操作完了。
如果压根就没有对全局数据进行操作,根本就不涉及可重入问题。
a++这个操作本身就不是安全的,这个操作实际上是3步
1、从内存加载到寄存器
2、在寄存器上进行累加操作
3、将数据再返回内存中,这个a++操作依然是可以被打断的。
线程安全不一定是可重入的,而可重入函数则一定是线程安全的,因为如果对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。
可重入函数和线程安全函数的区别