线程安全与可重入函数
一,什么是线程安全?
1,线程安全就是说多线程访问同一代码,不会产生不确定的结果。换句话说,线程安全就是多线程访问时,采用加锁机制,当一个线程访问该类的某个数据时,用锁对数据进行保护,其他线程不能访问该数据直到该线程读取完,其他线程才可使用,线程安全不会出现数据不一致或者数据污染。反之, 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
2,举个例子:
比如说,现在有一篮子苹果,有大有小,然后有一群人要来拿苹果了
在线程非安全情况下:
所有人一哄而上,变成了抢苹果了,因为大家都想要大的苹果,会发生冲突的。
在线程安全情况下:
把这一篮子苹果放到一个小屋里面,然后锁起来,然后每次只让一个人进去拿,等那个人拿完然后再让下一个人进去拿,这样保证了每个人都能拿到苹果,而且不会出现冲突。
3,有这么四类函数称为线程不安全的:
1>不保护共享变量的函数;
2>函数状态随着调用改变的函数;
3>返回指向静态变量指针的函数;
4>调用线程不安全函数的函数;
二,什么是可重入函数?
1,先看一个例子:
图中,main函数调用insert函数向一个链表head中插入节点node1,由于插入操作分为两步,刚做完第一步的时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换到sighandler函数,sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续 往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是,main函数和sighandler先后向链表中插入两个节点node1和node2,而最后node1真正插入链表了,node2丢失导致内存泄漏。
像上例这样,insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函 数,这称为重入。
insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入函数(Reentrant)。
2,重入函数的本质:两个不同的控制流程调用了全局堆中的共享数据,如果函数中都是局部变量则没有重入问题。
一个可重入函数需要满足的是:
1、不使用全局变量或静态变量;
2、不使用用malloc或者new开辟出的空间;
3、不调用不可重入函数;
4、不返回静态或全局数据,所有数据都有函数的调用者提供;
5、使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据;
如果一个函数符合以下条件之一则是不可重入的:
1>调用了malloc或free,因为malloc也是用全局链表来管理堆的。
2>调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。
3>SUS规定有些系统函数必须以线程安全的方式实现。
三,线程安全与可重入函数的区别与联系
函数可以是可重入的,也可以是线程安全的,或者两者皆是,或者两者皆非。不可重入函数不能由多个线程使用。
1、线程安全是在多线程情况下引发的,而可重入函数可以在只有一个线程的情况下发生。
2、线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
3、如果一个函数有全局变量,则这个函数既不是线程安全也不是可重入的。
4、如果一个函数当中的数据全身自身栈空间的,则这个函数即使线程安全也是可重入的。
5、如果将对临界资源的访问加锁,则这个函数是线程安全的;但如果重入函数的话加锁还未释放,则会产生死锁,因此不能重入。
6、线程安全函数能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据的操作不影响结果,使结果是相同的。
四,线程安全函数和可重入函数都有哪些?
1,APUE中,列出的可重入函数:
2,APUE上,描述的非线程安全函数: