一些关于线程的可重入和不可重入的知识

帮你了解一些关于线程的可重入和不可重入的知识。

线程的可重入和不可重入是指一个函数或者一个代码段是否可以被多个线程或者多次调用而不会引起错误或者不一致的结果。¹²

1.可重入:

如果一个函数或者一个代码段可以被多个线程或者多次调用,而且每次的执行结果都是相同的,那么就称为可重入的。¹²

可重入的函数或者代码段通常不会使用全局变量或者静态变量,也不会调用其他不可重入的函数或者代码段,而是只使用本地变量或者传入的参数。¹²

可重入的函数或者代码段可以保证线程安全,也就是说,不会因为多个线程或者多次调用而导致数据的混乱或者错误。¹²


2.不可重入:

如果一个函数或者一个代码段不能被多个线程或者多次调用,或者每次的执行结果可能会不同,那么就称为不可重入的。¹²

不可重入的函数或者代码段通常会使用全局变量或者静态变量,或者调用其他不可重入的函数或者代码段,而且没有对这些资源进行保护或者同步。¹²

不可重入的函数或者代码段可能会导致线程不安全,也就是说,因为多个线程或者多次调用而导致数据的混乱或者错误。¹²

例如,在C语言中,有一个标准库函数`strtok`,它用来将一个字符串分割成若干个子串。³ 这个函数使用了一个静态变量来保存上一次分割的位置,因此它是不可重入的。³ 如果有两个线程同时调用`strtok`来分割同一个字符串,那么它们可能会互相干扰,导致分割结果出错。³

如果你想了解更多关于线程的可重入和不可重入的知识,你可以参考

[这篇文章](^1^),它详细介绍了两者的概念和区别,并给出了一些例子和解释。¹

你也可以参考[这篇文章](^2^),它介绍了如何编写可重入的函数和代码段,并给出了一些注意事项和建议。

[这篇文章](^3^),它介绍了一些常见的不可重入的函数和原因,并给出了一些替代方案和解决方法。³

线程安全

基本定义

线程安全:简单来说线程安全就是多个线程并发同一段代码时,不会出现不同的结果,我们就可以说该线程是安全的;

线程不安全:说完了线程安全,线程不安全的问题就很好解释,如果多线程并发执行时会产生不同的结果,则该线程就是不安全的。

线程安全产生的原因:大多是因为对全局变量和静态变量的操作

常见的线程不安全的函数

(1)不保护共享变量的函数

(2)函数状态随着被调用,状态发生变化的函数

(3)返回指向静态变量指针的函数

(4)调用线程不安全函数的函数

常见的线程安全的情况

(1)每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的;

(2)类或者接口对于线程来说都是原子操作;

(3)多个线程之间的切换不会导致该接口的执行结果存在二义性;

#include<stdio.h>
#include<pthread.h>
 
int value=0;
 
void* func(void* arg){
        int i=0;
        while(i<10000){
                int tmp=value;
                value=i;
                printf("value is %d\n",value);
                value=tmp+1;
                i++;
        }
}
int main()
{
        pthread_t id1,id2;
        pthread_create(&id1,NULL,func,NULL);
        pthread_create(&id2,NULL,func,NULL);
        pthread_join(id1,NULL);
        pthread_join(id2,NULL);
        printf("value is %d\n",value);
        return 0;
}

运行结果(可见在存在线程安全时得到的结果并不是我们所期待的):

可重入函数

基本定义

重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的进程已经再次调用(执行流之间的相互嵌套执行);

可重入:多个执行流反复执行一个代码,其结果不会发生改变,通常访问的都是各自的私有栈资源;

不可重入:多个执行流反复执行一段代码时,其结果会发生改变;

可重入函数:当一个执行流因为异常或者被内核切换而中断正在执行的函数而转为另外一个执行流时,当后者的执行流对同一个函数的操作并不影响前一个执行流恢复后执行函数产生的结果;

不可重入函数:当程序运行到某一个函数的时候,可能因为硬件中断或者异常而使得在用户正在执行的代码暂时终端转而进入你内核,这个时候如有一个信号需要被处理,而处理的这个信号的时候又会重新调用刚才中断的函数,如果函数内部有一个全局变量需要被操作,那么,当信号处理完成之后重新返回用户态恢复中断函数的上下文再次继续执行的时候,对同一个全局变量的操作结果可能就会发生改变而并不如我们预期的那样,这样的函数被称为不可重入函数。例如在进行链表的插入时,插入函数访问一个全局链表,有可能因为重入而造成错乱。

可重入函数满足条件

(1)不使用全局变量或静态变量;

(2)不使用用malloc或者new开辟出的空间;

(3)不调用不可重入函数;

(4)不返回静态或全局数据,所有数据都有函数的调用者提供;

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

不可重入函数符合以下条件之一

(1)调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的。

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

(3)可重入体内使用了静态的数据结构。

可重入函数分类

(1)显式可重入函数

如果所有函数的参数都是传值传递的(没有指针),并且所有的数据引用都是本地的自动栈变量(也就是说没有引用静态或全局变量),那么函数就是显示可重入的,也就是说不管如何调用,我们都可断言它是可重入的。

(2)隐式可重入函数

可重入函数中的一些参数是引用传递(使用了指针),也就是说,在调用线程小心地传递指向非共享数据的指针时,它才是可重入的。

可重入函数可以有多余一个任务并发使用,而不必担心数据错误,相反,不可重入函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在 代码的关键部分禁用中断)。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据,可重入函数要么使用本地变量,要么在使用全局变量时保护自己 的数据。

代码演示:

#include<stdio.h>
#include<signal.h>
 
int value=0;
 
void fun(){
        int i=0;
        while(i++<5){
                value++;
                printf("value is %d\n",value);
                sleep(1);
        }
}
int main()
{
        signal(2,fun);
        fun();
        printf("the value is %d\n",value);
        return 0;
}

运行结果对比:

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

联系:

函数可以是可重入的,是线程安全的,或者二者皆是,或者二者皆非。

不可重入的函数不能由多个线程使用。另外,或许不可能让某个不可重入的函数是线程安全的。

区别:

(1)可重入函数是线程安全函数的一种,其特点在于它们被多个线程调用时,不会引用任何共享数据。

(2)线程安全是在多个线程情况下引发的,而可重入函数可以在只有一个线程的情况下来说。

(3)线程安全不一定是可重入的,而可重入函数则一定是线程安全的

(4)如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的

(5)如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。

(6)线程安全函数能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据的操作互不影响使结果是相同的。

(1) 可重入(reentrancy) vs. 线程安全(thread-safety) - 知乎. https://zhuanlan.zhihu.com/p/352218464.
(2) linux系统编程—深入理解线程安全和可重入函数 - 知乎. https://zhuanlan.zhihu.com/p/148496930.
(3) 线程 重入_线程重入_不知者不罪的博客-CSDN博客. https://blog.csdn.net/u014325402/article/details/78314601.
(4) 可重入(reentrancy) vs. 线程安全(thread-safety) - 知乎. https://zhuanlan.zhihu.com/p/352218464.
(5) linux系统编程—深入理解线程安全和可重入函数 - 知乎. https://zhuanlan.zhihu.com/p/148496930.
(6) 线程 重入_线程重入_不知者不罪的博客-CSDN博客. https://blog.csdn.net/u014325402/article/details/78314601.
(7) 线程安全与可重入函数的区别与联系 - CSDN博客. https://blog.csdn.net/ypt523/article/details/80380272.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值