线程安全和可重入函数

来源:互联网 发布:apache ab post json 编辑:程序博客网 时间:2024/06/16 23:10
线程安全与可重入
  • 定义

  线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全。

  可重入:可重入代码也叫纯代码(Pure code)是一种允许多个进程同时访问的代码。为了使各进程所执行的代码完全相同,故不允许任何进程对其进行修改。程序在运行过程中可以被打断,并由开始处再次执行,并且在合理的范围内(多次重入,而不造成堆栈溢出等其他问题),程序可以在被打断处继续执行,且执行结果不受影响

如果fun(),中,使用了static变量、返回全局变量、调用非可重入函数等等,带有全局性的操作,都将会导致2次以上调用fun()的结果的不可再现性(当然,有些时候使用了static、全局变量等等,不一定导致调用结果不可再现性)。只要使调用结果具有可再现性,那么该函数就是可重入的。

 

为了保证函数是可重入的,需要做到一下几点:

1,不在函数内部使用静态或者全局数据

2,不返回静态或者全局数据,所有的数据都由函数调用者提供

3,使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据

4, 如果必须访问全局数据,使用互斥锁来保护

5,不调用不可重入函数

线程安全,是针对多线程而言的。那么和可重入联系起来,我们可以断定,可重入函数必定是线程安全的,但是线程安全的,不一定是可重入的。不可重入函数,函数调用结果不具有可再现性,可以通过互斥锁等机制,使之能安全的同时被多个线程调用,那么,这个不可重入函数就是转换成了线程安全。

线程安全,描述的是函数能同时被多个线程安全的调用,并不要求调用函数的结果具有可再现性。也就是说,多个线程同时调用该函数,允许出现互相影响的情况,这种情况的出现需要某些机制比如互斥锁来支持,使之安全。


线程不安全怎么能够改写成线程安全


1.仍旧使用共享数据,但是在使用共享数据的时候做同步操作。对于函数过程中使用的共享数据,可以进行简单的PV操作,对于返回结果可以在PV操作中把共享数据拷贝到非共享的位置,以便及时释放共享变量。

2.杜绝使用共享变量。也就是说把函数改成了可重入的函数。但是,彻底杜绝共享变量有的时候不容易做到。对于上面的提到的第一种和第三种情况很容易做到,但是对于第二种情况,我们没有办法。所以,对于一个不接受引用和指针的函数,我们可以把它做到绝对的可重入,但是,对于一个接受指针或者引用的函数来说,对不起,我们不能确保他肯定是可重入的。


  •  关于性能

  通常来说,多线程是为了在同一时间内能够处理更多的同样类型的事情,但是线程不安全却阻碍了我们达到我们的目的。所以,我们有的时候不得不想方设法的把线程不安全的函数改写成线程安全的。

  改写的结果无非两种,一种是原函数的“同步版本”,一种是原函数的“可重入版本”可重入版本相比前者的从性能上来说有着天然的优势,这种优势就是在于它不涉及PV操作,不存在软件上的瓶颈,可以最大化的利用硬件资源。然而“同步版本”则有可能不能充分利用硬件的资源,因为程序在等待资源的释放。

  • 关于重写的策略

  拿到一个线程不安全函数是一件郁闷的事情。但是有的时候你必须要使用它,那怎么办呢?这个时候就要从上面提到的三个可能的共享数据来入手了

  1. 如果调用函数的返回结果是共享的,这个时候就要使用上面蓝色字体提到的lock-and-copy方法,在PV操作中,把结果转移到非共享的区域。
  2. 尝试在调用函数的时候不使用引用或者指针,如果函数在多线程工作的时候结果正确了,则为题解决了,否则,问题可能出现在函数内部。
  3. 找到这个函数的共享变量,对其做PV操作。

原创粉丝点击