linux:线程安全与可重入函数

来源:互联网 发布:网络电视装机必备 编辑:程序博客网 时间:2024/04/27 21:50

前面我们已经介绍过线程的概念,我们知道,在同一个进程里,可能同时会有多个线程同时运行,这时候我们就需要考虑线程安全问题了。

线程安全:

多个线程并发执行时,不会产生不确定的结果,其结果是可预知的且与单线程执行结果相同(通常采用加锁机制),或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。

线程安全问题通常都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。 线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

线程不安全:

上面介绍了线程安全,线程不安全就很好解释了,当多个线程并发执行时,没有对数据加以保护,会出现不确定的结果,,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。那么线程就不安全。

常见的线程不安全的函数:

(1)不保护共享变量的函数
(2)函数随着被调用,状态发生变化的函数
(3)返回指向静态变量指针的函数
(4)调用线程不安全的函数的函数

常见的线程安全函数:

(1)每个线程对全局变量或者静态变量只有读操作没有写操作,一般来说这样的线程是安全的。
(2)类或者接口对线程来说都是院子操作
(3)多个线程之间的切换不会导致该接口的执行结果存在二义性

来看具体代码:
这里写图片描述

上面的程序中。我们让两个线程同时访问fun函数,我们期待得到的结果是1000,看运行结果:

这里写图片描述

结果并不是我们期待得到的,这就是线程的不安全。

可重入函数

(1)重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的进程已经再次调用(执行流之间的相互嵌套执行);

(2)可重入:多个执行流反复执行一个代码,其结果不会发生改变,通常访问的都是各自的私有栈资源;

(3)不可重入:多个执行流反复执行一段代码时,其结果会发生改变;

(4)可重入函数:当一个执行流因为异常或者被内核切换而中断正在执行的函数而转为另外一个执行流时,当后者的执行流对同一个函数的操作并不影响前一个执行流恢复后执行函数产生的结果;

(5)不可重入函数:当程序运行到某一个函数的时候,可能因为硬件中断或者异常而使得在用户正在执行的代码暂时终端转而进入你内核,这个时候如有一个信号需要被处理,而处理的这个信号的时候又会重新调用刚才中断的函数,如果函数内部有一个全局变量需要被操作,那么,当信号处理完成之后重新返回用户态恢复中断函数的上下文再次继续执行的时候,对同一个全局变量的操作结果可能就会发生改变而并不如我们预期的那样,这样的函数被称为不可重入函数。例如在进行链表的插入时,插入函数访问一个全局链表,有可能因为重入而造成错乱。

可重入函数满足条件

(1)不使用全局变量或静态变量;
(2)不使用用malloc或者new开辟出的空间;
(3)不调用不可重入函数;
(4)不返回静态或全局数据,所有数据都有函数的调用者提供;
(5)使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据;

不可重入函数符合以下条件之一

(1)调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的。
(2)调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构。
(3)可重入体内使用了静态的数据结构。

可重入函数可以有多余一个任务并发使用,而不必担心数据错误,相反,不可重入函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在 代码的关键部分禁用中断)。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据,可重入函数要么使用本地变量,要么在使用全局变量时保护自己 的数据。

先来看下面的代码:
这里写图片描述

运行结果:
这里写图片描述

而当我们在程序运行过程中给出2号信号,即Ctrl+C时,结果变成:
这里写图片描述

每收到一次信号,进程都会去调用fun函数,而g_val是全局变量,因此,最终结果变成15。那么如果g_val是局部变量呢?

这里写图片描述

这里写图片描述

此时运行结果和我们预期的一致。因此,在上述程序中,不会影响结果的fun函数是可重入函数。

总结:
一个可重入函数内部使用的数据都应该来自于自身的栈空间,包括返回值也不应该是全局或者静态的;而正是因为其中的操作数据都来自于自身的栈空间,而每次调用函数会开辟不同的栈空间,因此二者互不影响。

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

联系:

函数可以是可重入的,是线程安全的,或者二者皆是,或者二者皆非。不可重入的函数不能由多个线程使用。

区别:

(1)可重入函数是线程安全函数的一种,其特点在于它们被多个线程调用时,不会引用任何共享数据。

(2)线程安全是在多个线程情况下引发的,而可重入函数可以在只有一个线程的情况下来说。

(3)线程安全不一定是可重入的,而可重入函数则一定是线程安全的。

(4)如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。

(5)如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。

(6)线程安全函数能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据的操作互不影响使结果是相同的。

原创粉丝点击