【Linux】可重入函数与线程安全

可重入函数


1. 概念

重入函数: 函数被不同的控制流调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入函数。
可重入函数:不同的控制流重入函数时,不会造成数据二义性。具体来说就是函数内部的数据都应该来自自身的栈空间,不应该有全局或者静态的变量。
不可重入函数:重入函数会造成数据的二义性,导致对一个变量的操作变得无法预期。称为不可重入函数。
接下来我们简单的分别举上一个可重入函数和不可重入函数的例子。

2.示例

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

这里写图片描述
分析上面的代码和执行结果,fun函数里对全局变量进行++操作。主函数里会捕捉2号信号(即ctrl+c)信号,捕捉后执行自定义函数fun。同时主函数里也调用了fun函数。所以,当我们运行test后,在主函数调用fun函数还没有完成时,从键盘输入ctrl+c产生2号信号,程序从内核态切回用户态之前会检查有无信号需要处理,发现有了一个2号信号,便先去处理2号信号,对它的处理方式是自定义函数fun,于是重入fun函数,执行++。信号处理完毕,从内核态转回用户态,继续执行刚才还没有完成的主函数对于fun函数的调用。所以,本来应该加到5的count,最后加到了10。造成了数据的二义性,这样的函数称为不可重入函数。

我们把上面的代码改一下,将count变量设置为局部变量,再次运行。
这里写图片描述
这样,两个执行流并没有互相影响,这时的fun函数称为可重入函数。

3.总结

一个可重入函数需要满足以下几个要求:
1、不使用全局变量或静态变量;
2、不使用用malloc或者new开辟出的空间;(malloc使用的是全局的链表来维护堆)
3、不调用不可重入函数;
4、不返回静态或全局数据,所有数据都由函数的调用者提供;
5、一定要用全局数据的话可以在函数内部进行全局的局部拷贝;

线程安全


1.概念

线程安全:多个线程访问同一个区域的时候其最终结果是可预期的,并不会因为产生冲突或者异常中断再次恢复而使结果不可预期。

例如上一篇博客我在模拟生产者消费者模型时,使用了信号量来保证线程的安全。如果一段会被多个线程访问的代码操作本身并不是原子的,那我们就必须利用互斥锁或信号量等对该段代码进行保护,避免产生数据二义性的问题。一个函数被称为线程安全的,当且仅当被多个并发线程反复调用时,它会一直产生正确的结果。

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

1、线程安全是在多线程情况下引发的,而可重入函数可以在只有一个线程的情况下发生。
2、线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
(例如:对临界资源的访问加锁,使函数是线程安全的,但如果重入函数时加锁还未释放,则会产生死锁,因此不能重入。)
3、如果一个函数有全局变量,则这个函数既不是线程安全也不是可重入的。
4、如果一个函数当中的数据全身自身栈空间的,则这个函数即是线程安全也是可重入的。
5、线程安全函数能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据的操作不影响结果,使结果是相同的。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值