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

本文探讨了线程安全和可重入函数的概念。线程安全函数在多线程环境中调用不会产生错误,而可重入函数允许同一时间被多个线程调用。可重入函数是线程安全的,但线程安全函数不一定是可重入的。两者之间的关键区别在于是否涉及共享数据。文章还列举了示例和条件,强调了确保线程安全和可重入性的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、什么是线程安全?
     一个函数被称为线程安全的(thread-safe),当且仅当被多个并发进程反复调用时,它会一直产生正确的结果。反之,如果一个函数不是线程安全的,我们就说它是线程不安全的(thread-unsafe)。所以,有这么四类函数称为线程不安全的:
- 不保护共享变量的函数;
- 函数状态随着调用改变的函数;
- 返回指向静态变量指针的函数;
- 调用线程不安全函数的函数;
 
      如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和 运行的结果是一样的,而且其他的变量的值也和预期的是一样  的, 就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
     线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
2、什么是可重入函数?
     可重入函数即表示可以被多个执行流重复进入,意味着只使用自己栈上的变量,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。
    一个不可重入函数的例子:
   int global;
   int fun( int a )
   {
      int temp;
      global = a;
      temp = gloabl*2;
      return temp;
   }
        global是一个全局变量,若进程a运行这段代码传入的参数是2,预期的结果是4;进程b也运行这段代码,传入的参数是3,由于操作系统的进程调度,进程a中的global就有可能被覆盖,结果不一定是预期结果。
        要想保证函数是可重入函数,在函数体内不能访问全局变量,不使用静态局部变量,(静态变量和全局变量都存在静态区)坚持只使用局部变量,写出的函数就将是可重入的。如果必须访问全局变量,可以利用互斥信号量来保护全局变量。
       可重入函数是线程安全函数的一种,其特点在于它们被多个线程调用时,不会引用任何共享数据。函数是可重入(reentrant)的,是指对于相同的(并且合法的)函数参数(包括无参函数的情况),多次调用此函数产生的行为是可预期的,即函数的行为一致,或者结果相同。不能保证这一点的函数称为不可重入(non-reentrant)函数。
它又分为这么两种:
(1)显式可重入函数
       如果所有函数的参数都是传值传递的(没有指针),并且所有的数据引用都是本地的自动栈变量(也就是说没有引用静态或全局变量),那么函数就是显示可重入的,也就是说不管如何调用,我们都可断言它是可重入的。
(2)隐式可重入函数
       可重入函数中的一些参数是引用传递(使用了指针),也就是说,在调用线程小心地传递指向非共享数据的指针时,它才是可重入的。
       可重入函数可以有多余一个任务并发使用,而不必担心数据错误,相反,不可重入函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在 代码的关键部分禁用中断)。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据,可重入函数要么使用本地变量,要么在使用全局变量时保护自己 的数据。
     可重入函数的条件:
          1.不在函数内部使用静态或全局数据
          2.不返回静态或全局数据,所有数据都有函数的调用者提供
          3.使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
          4.不调用不可重入函数
       可见,可重入函数的可重入性是有时是调用者与被调用这共有的属性,并不只是被调用者的属性。可重入和线程安全(Thread-Safe)是两个不同的概念:可重入函数一定是线程安全的;线程安全的函数可能是重入的,也可能是不重入的;线程不安全的函数一定是不可重入的。
可重入函数要解决的问题是:不在函数内部使用静态或全局数据,不返回静态或全局数据,也不调用不可重入函数。
线程安全函数要解决的问题是:多个线程调用函数时访问资源冲突。
      函数如果使用静态变量,通过加锁后可以转成线程安全函数,但仍然有可能不是可重入的。
      比如:strtok函数是既不可重入的,也不是线程安全的;加锁的strtok不是可重入的,但线程安全;而strtok_r既是可重入的,也是线程安全的。
3、两者的区别与联系
     函数可以是可重入的,是线程安全的,或者二者皆是,或者二者皆非。不可重入的函数不能由多个线程使用。另外,或许不可能让某个不可重入的函数是线程安全的。
    1、可重入函数是线程安全函数的一种,其特点在于它们被多个线程调用时,不会引用任何共享数据。
可重入函数与线程安全的区别与联系:
  2、线程安全是在多个线程情况下引发的,而可重入函数可以在只有一个线程的情况下来说。
  3、线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
  4、如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。
  5.如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。
  6、线程安全函数能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据的操作互不影响使结果是相同的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值