文章目录
- 前言
- 一. 场景
- 二. 可重入与线程安全
- 结束语
前言
在Linux中,进程/线程可能因为时间片到达,或者其他中断,或者调用系统,需要从用户态切换到内核态,而内核空间会保存切换前,用户代码执行处的上下文,以便切换回用户态时,可以继续执行原来的代码。
而这样的切换是否会影响代码的执行呢?这就是可重入函数和不可重入函数的概念的产生
一. 场景
以下是用户态的一次执行
假如其中有一个链表头插函数的调用
在申请结点空间,将node1的指针指向head->next,还没作head的改变时,假如,时间片到了,或者发生了其他的中断,切换到了内核态,捕捉信号。
而信号的执行函数中也同样使用了链表头插的函数,就会再申请一个node2的空间,完成头插后,最终切换回用户态,然后继续执行用户态的链表头插的函数,还会将head->next=node1,这样node2的失效了。就发生了内存泄漏
所以有些函数是不允许执行到一半,而切换到执行流的,这类函数称为不可重入函数
,反之为可重入函数
只要函数内部有和文件的交互
,使用了全局数据
,或者有malloc
,new
,申请了堆数据,那都是不可重入函数
如果一个函数内部只使用局部变量,那么这个函数就可以是可重入函数
可重入/不可重入不是
函数的优缺点,只是函数的特性
二. 可重入与线程安全
在多线程时,我们往往需要注意线程的互斥与同步,而也就是在多线程中,不同线程并发运行,经常出现一个线程的动作还没完成,时间片到达,不得不切换成其他线程的情况。
所以线程安全和函数是否可重入有着一定的关系
接下来我们做些总结
- 线程安全:多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操作,并且没有锁保护的情况下,会出现问题。
- 重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他执行流再次进入,我们称之为重入。
常见的线程不安全的情况:
- 不保护共享变量的函数
- 被调用后,状态会发生变化的函数
- 返回指向静态变量指针的函数
- 调用线程不安全的函数
常见的线程安全的情况:
- 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说是线程安全的
- 类或者接口对于线程来说是原子操作
- 多个线程之间的切换不会导致该接口执行结果存在二义性
常见的不可重入的情况:
- 调用了malloc/free函数,因为malloc函数是使用全局链表来管理堆的
- 调用了标准I/O库函数,标准I/O库的很多实现都是以不可重入的方式使用全局数据结构
- 可重入函数体内使用了静态的数据结构
常见的可重入的情况:
- 不使用全局变量或静态变量
- 不使用malloc或者new 开辟空间
- 不调用不可重入函数
- 不返回静态或者全局数据,所以数据都有函数的调用者提供
- 不使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
可重入与线程安全的联系:
- 如果调用的函数是
可重入函数
,那么此时是线程安全
的 - 如果调用的函数是不可重入的,多线程访问就会引发线程安全问题
- 如果一个函数中有全局变量,那么这个函数既不是线程安全的,也不是可重入的
可重入函数是线程安全的一种方式,线程安全不一定是可重入的,但可重入一定是线程安全的
结束语
感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。