前言:在多线程的初步学习中,有两个概念时常被一起提到,是否可重入与线程是否安全,由于这两者有一定的关联性,就有部分的同学将其混为一谈。
一、线程安全
导致线程安全的本质是由于,多线程的执行流的顺序的不确定性,而这个顺序的不确定性对于其本身的局部或作用域只在线程内部的资源并无影响,但多线程本身具有资源共享的特点,若多线程内对于共享资源有了某些改动可能的结果就是不确定的,这结果不确定可是个大问题。
简而言之,多个线程并发时,对全局变量或者静态变量进行操作,可能出现不同的结果。
常见的线程不安全的情况
- 不保护共享变量的函数
- 函数状态随着被调用,状态发生变化的函数
- 返回指向静态变量指针的函数
- 调用线程不安全函数的函数
常见的线程安全的情况
- 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的
- 类或者接口对于线程来说都是原子操作
- 多个线程之间的切换不会导致该接口的执行结果存在二义性
二、可重入函数
一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重
入函数,否则,是不可重入函数。
我们从字面意思上理解,可重入就是可以重复进入的函数,这里的重复进入不是递归函数的函数回调,而是在不同线程同时执行的函数的意思,即同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入。
常见不可重入的情况
- 调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的
- 调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构
- 可重入函数体内使用了静态的数据结构
常见可重入的情况
- 不使用全局变量或静态变量
- 不使用用malloc或者new开辟出的空间
- 不调用不可重入函数
- 不返回静态或全局数据,所有数据都有函数的调用者提供
- 使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
三、可重入与线程安全
可重入与线程安全联系
- 函数是可重入的,那就是线程安全的
- 函数是不可重入的,那就不能由多个线程使用,有可能引发线程安全问题
- 如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。
可重入与线程安全区别
- 可重入函数是线程安全函数的一种。
- 线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
- 如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。