可重入与线程安全

可重入与线程安全(整理自网上资料)

线程安全函数:

概念:
  线程安全的概念比较直观。一般说来,一个函数被称为线程安全的,当且仅当被多个并发线程反复调用时, 它会一直产生正确的结果。
确保线程安全:
  要确保函数线程安全,主要需要考虑的是线程之间的共享变量。属于同一进程的不同线程会共享进程内存空 间中的全局区和堆,而私有的线程空间则主要包括栈和寄存器。因此,对于同一进程的不同线程来说,每个线程的局部变量都是私有的,而全局变量、局部静态变 量、分配于堆的变量都是共享的。在对这些共享变量进行访问时,如果要保证线程安全,则必须通过加锁的方式,以实现对它们的串行访问。
线程不安全的后果:
  线程不安全可能导致的后果是显而易见的——共享变量的值由于不同线程的访问,可能发生不可预料的变化(比如说i++在多线程的情况下就容易出问题),进而导致程序的错误,甚至崩溃。

可重入函数:

概念:

  在多线程或有异常控制流(异步信号、中断、异常等处理过程?)的情况下,当某个函数运行到中途时,控制流(也就是当前指令序列)就有可能被打断而去执行另一个函数.而"另一个函数"很有可能是它本身.如果在这种情况下不会出现问题,比如说数据或状态不会被破坏,行为确定。那么这个函数就被称做"可重入"的.

确保可重入:
  要确保函数可重入,需满足一下几个条件:
  1、不在函数内部使用静态或全局的非const变量 。
  2、不返回静态或全局数据,所有数据都由函数的调用者提供。 
  3、使用本地数据,或者通过制作全 局数据的本地拷贝来保护全局数据。
  4、不调用不可重入函数。

  5、不依赖任何单个资源的锁(mutex等)。
不可重入的后果:
  不可重入的后果主要体现在象信号处理函数这样需要重入的情况中。如果信号处理函数中使用了不可重入的 函数,则可能导致程序的错误甚至崩溃。

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

  一个函数,不re-entrant,有两种方式,一种是,虽然语法上可以同时运行,但是由于操作公共资源(C中全局,静态变量),导致结果不定,这种也是 不thread-safe的。另一种是,语法上就是不可同时运行的,比如用了MUTEX来锁公共资源,虽然函数可以同时调用,但却是串行的,这就是 thread-safe的。
线程不安全函数可以使用锁实现线程安全,但是却不一定是可重入的,例如线程被中断,且中断处理程序中又申请同样的锁,则造成死锁,是不可重入的。

  任何线程不安全问题的根源都是“共享数据”。所以,不使用任何共享数据的函数(即:可重入函数)肯定是线程安全的。反之不成立。

结论:

1. reentrant是对函数相当严格的要求,绝大部分函数都不是reentrant的(APUE上有一个reentrant函数的列表).什么时候我们需要reentrant函数呢?只有一个函数需要在同一个线程中需要进入两次以上,我们才需要reentrant函数.这些情况主要是异步信号处理,递归函数等等.(non-reentrant的递归函数也不一定会出错,出不出错取决于你怎么定义和使用该函数). 大部分时候,我们并不需要函数是reentrant的.
2. 在多线程环境当中,只要求多个线程可以同时调用一个函数时,该函数只要是thread safe的就可以了.我们常见的大部分函数都是thread safe的,不确定的话请查阅相关文档.
3. reentrant和thread safe的本质的区别就在于,reentrant函数要求即使在同一个线程中任意地进入两次以上,也能正确执行.大家常用的malloc函数是一个典型的non-reentrant但是是thread safe函数,这就说明,我们可以方便的在多个线程中同时调用malloc,但是,如果将malloc函数放入信号处理函数中去,这是一件很危险的事情.
4. reentrant函数肯定是thread safe函数
也就是说,non thread safe肯定是non-reentrant函数。不能简单的通过加锁,来使得non-reentrant函数变成 reentrant函数"使用的全局变量的函数也不一定是不可重入的。"这句是正确的,只要正确使用就可以了,但是不使用全局变量是写可重入函数的简单方法.


你可能感兴趣的:(可重入与线程安全)