在之前的博文中有说过线程,线程是一个执行流,一个指令序列,一个指令分支,为了共享资源而产生的线程,但只要是资源,都会出现资源的数目与操作安全问题,所以在线程这一块,设计了信号量,条件变量,各种锁,来保证资源的访问独占性与原子性。

    什么是线程安全?

      线程安全指的是在多线程访问情况下,保证线程对临界资源的操作原子性,在临界区中,不允许其他的线程进行插入访问造成了数据的乱序占用而修改,产生逻辑结果不一致的问题。

      线程不安全就是不提供数据访问保护,出现多个线程先后更改数据造成脏数据。

    其实对于线程安全简单来说,就是保证在多线程情况下,我们进行的操作,不论是否控制临界区的插入访问,只要保证数据逻辑结果的原子性就是线程安全的,但若出现数据的乱序操作或者中断操作,达不到预期的结果则就是线程不安全的。


    那么对于线程安全来说,我们通过各种控制临界区的执行权限来进行限制,可以阅读之前的博文来进行线程的同步与互斥,线程本身就是为了共享资源而产生,利用同步与互斥来进行任务逻辑上的运行,既保证协同,有保证逻辑独有。

    

    线程就是一个执行流,一个指令序列,一个指令分支,也就是一个函数。那么对于函数而言,存在着不需要进行互斥控制的情况,也就是在多线程情况下,无论线程之间是如何运行插入修改,都能够保证数据的正确性,就是可重入函数。信号捕捉也是进行函数的操作,所以信号跟线程在原理上是相似的。

   

先举一个不可重入函数的例子:

 wKiom1cvIIyR3Fj9AACYMWS253c132.png

针对这张图片,我们来分析一下他的过程。

  1. 在insert函数中,我们很明显的知道,他是一个头插的函数,

  2. 然后在insert函数执行之中,倘若在p->next = head之后,head = p之前产生了一个信号捕捉,或者同时运行多个线程,在多个线程之中,线程中断插入在这之间,就会出现图片上的结果。

  3. 也就是我们要达到的目的是我们要插入2个数据,但是却只插入了一个数据,另外一个数据出现了空间浪费,目的不达到,并产生了脏数据。

    insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入,insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant) 函数。

    

为什么两个不同的控制流程调用同一个函数,访问它的同一个局部变量或参数就不会造成错乱?

    在线程之中,线程虽然强调资源共享,但是他们的栈却是独有的,所以访问它的同一个局部变量或参数就不会造成错乱。


不可重入函数的条件:

调用了malloc或free,因为malloc也是用全局链表来管理堆的。

调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

SUS规定有些系统函数必须以线程安全的方式实现,这里就不列了。



总结:

    对于线程安全来说,就是保证在多线程逻辑环境下的数据操作不产生副作用影响,从而出现了各种保证线程安全的操作,条件变量,信号量,各种锁。

    线程实际上是一个指令序列,一个执行流,一个函数。所以对于线程之中,若在多线程环境下不论怎么中断插入,都不影响数据逻辑的函数叫做可重入函数。