线程安全与可重入函数

来源:互联网 发布:查看产品淘宝客佣金 编辑:程序博客网 时间:2024/06/05 05:31

1.什么是可重入函数和不可重入函数?

    可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量,静态变量等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。

    这么说,可能还是不太好理解。接下来,我举个例子。


    main函数调用insert函数,向一个全局链表head中头插一个node1,插入操作分为两步,第一步p->next = head,第二步head = p,当第一步接收完成后,node1->next = head,恰好这时收到一个信号,由于硬件中断使进程切到内核态,再次回到用户态时检查到有信号待处理,于是切到用户态调用sighandler函数,而sighandler函数中也调用了insert函数,向全局链表head中头插一个node2,插入操作完成后,此时head指向node2,node2指向下一个结点,node2成功插入。进程切回内核态,sighandler函数返回。接着返回用户态,从main函数中的insert的第二步开始执行,也就是head = node1这一步,这一步一执行完后,刚刚插好的node2不见了,这就出现了问题,产生了数据的不一致性。

   像上面insert这种函数,由于使用了一些系统资源,比如全局变量,静态变量等,被两个不同的执行流执行,如果它被中断的话,可能会出现问题,就是不可重入函数。而可重入函数不会出现什么错误。

    这里来说一下可重入函数和不可重入函数的条件,我们可以看到出现上述问题的本质其实是该函数访问了一个全局链表,而且并没有对这个链表进行保护。所以可重入函数和不可重入函数的条件也是基于这个本质的。

     不可重入函数:

            1.访问静态变量或全局变量

            2.调用了malloc或free(因为malloc也是用全局链表来管理堆的)

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

    可重入函数:
    1.不在函数内部使用静态或全局数据
    2.不返回静态或全局数据,所有数据都有函数的调用者提供
    3.使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
    4.不调用不可重入函数

2.什么是线程安全和线程不安全?

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。通俗的说就是如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
  比如一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
  在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;而如果是在多线程情况下,比如有两个线程,线程 A 先将元素1存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B向此 ArrayList 添加元素2,因为此时 Size 仍然等于 0 (注意,我们假设的是添加一个元素是要两个步骤,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值,结果Size等于2。
  那好,我们来看看 ArrayList 的情况,期望的元素应该有2个,而实际只有一个元素,造成丢失元素,而且Size 等于 2。这就是“线程不安全”了。


3.区别与联系

我们来对比一下线程安全问题和可重入函数问题:

联系:1.函数可以是可重入的,是线程安全的,或者二者皆是,或者二者皆非。不可重入的函数不能由多个线程使用。另外,或许不可能让某个不可重入的函数是线程安全的。

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

    3.可重入函数和线程安全解决的都是对全局或静态数据访问的不一致性。

区别:

   1.线程安全能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据的操作互不影响使结果是相同的。
     2.线程安全是在多个线程情况下引发的,而可重入函数可以在只有一个线程的情况下来说。
     3.线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
     4.如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。


  


    

原创粉丝点击