可重入函数与线程安全

在前面的学习中,我们已经介绍过了可重入函数,下面我们先来复习一些基本的概念:

  • 重入:即重复进入。同一个函数被不同的执行流调用,有可能出现第一次调用还未返回,就再次进入该函数开始了下一次的调用。
  • 可重入:函数可由多个线程并发使用,而不必担心数据错误。可重入函数可以在任意时刻被中断,稍后再继续运行时并不会丢失数据。可重入性解决函数运行结果的确定性和可重复性。
  • 可重入函数:如果一个函数只访问自己的局部变量或参数,则称为可重入函数。
  • 不可重入:当程序被多个线程反复调用,产生的结果出现错误。
  • 不可重入函数:当函数访问一个全局的变量或者参数时,有可能因为重入而造成混乱,这样的函数称为不可重入函数。
  • 可重入函数编写规范:

        (1)不在函数内部使用静态或全局数据;

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

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

        (4)如果必须访问全局变量,利用互斥机制来保护全局变量;

        (5)不调用不可重入函数。

  • 如果一个函数符合以下条件之一,则是不可重入的:

        (1)函数中使用了静态变量,无论是全局静态变量还是局部静态变量;

        (2)函数返回静态变量;

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

        (4)函数体内使用了静态的数据结构;

        (5)函数体内调用了malloc()或者free()函数;

        (6)函数体内调用了其他标准I/O函数;

    总的来说,如果一个函数在重入条件下使用了未受保护的共享资源,那么它是不可重入的。

  • 线程安全:如果一个函数在同一时刻可以被多个线程安全地调用,则称该函数是线程安全的。
  • 线程安全函数解决多个线程调用函数时访问共享资源的冲突问题。
  • 可重入与线程安全的关系:

        (1)如果一个函数当中有全局变量,则该函数既不是线程安全的也不是可重入的;

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

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

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

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

  • 为了写一个稳定的多线程程序,必须遵守线程安全,但不一定遵守可重入。
  • 可重入函数是线程安全函数的一个真子集。也就是说,如果函数是可重入的,就可以保证它是线程安全的。

示例:

在多线程条件下,函数应当是线程安全的,进一步更强的条件是可重入的。可重入函数保证了在多线程条件下,函数的状态不会出现错误。以下分别是一个不可重入和可重入函数的示例:

static  int  tmp;
void  func1(int  *x,int  *y){
     tmp = *x;
     *x = *y;
     *y = tmp;
}

void  func2(int  *x,int  *y){
     int tmp;
     tmp = *x;
     *x = *y;
     *y = tmp;
}

func1是不可重入的,func2是可重入的。因为在多线程条件下,操作系统会在func1还没有执行完的情况下,切换到另一个线程中,那个线程可能再次调用func1,这样状态就错了。

代码实例:

#include <stdio.h>
#include<signal.h>

int g_val = 0;

void fun(){
    int i = 3;
    while(i--){
        g_val++;
        printf("g_val = %d\n",g_val);
        sleep(1);
    }
} 

int main(){
    signal(2,fun);//2号信号:ctrl+c。收到2号信号,执行自定义处理动作
    fun();
    printf("main : g_val = %d\n",g_val);
}

结果演示:


本代码原本想实现最终g_val为3,结果在为2时,我们按下“Ctrl+c”,产生了错误。由于变量是全局的,收到2号信号后,继续进行+1运算,最终执行结果为6。因此该程序中,fun函数是不可重入函数。

为了防止这种情况的发生,我们可以将g_val改为局部变量。这样函数被二次调用时,两次结果最终并不影响并且相等。



             



猜你喜欢

转载自blog.csdn.net/cecilia3333/article/details/79873894