线程安全与可重入函数
更新日期:
1、可重入函数与不可重入函数
当捕捉到信号时,不论进程的主控制流程当前执行到哪,都会先跳到信号处理函数中执行,从信号处理函数返回后再继续执行主控制流程。信号处理函数是一个单独的控制流程,因为他和主控制流程是异步的,二者不存在调用和被调用的关系,并且使用不同的堆栈空间。引入了信号处理函数使得一个进程具有多个控制流程,如果这些控制流程访问相同的全局资源(全局资源、硬件资源等就有可能出现冲突,如下面的例子所示。)
举例:假如程序执行到某个函数fun()时,收到信号,于是暂停目前正在执行的函数,转到信号处理函数,而这个信号处理函数的执行过程中,又恰恰也会进入fun()函数,这样就发生了 “重入”,此时如果能够正确的运行,而且处理完成后,之前暂停的fun()也能够正确运行,则说明它是可重入的。反之,不可重入。
确保函数可重入的条件
1、不在函数内部使用静态或全局数据
2、不返回静态或全局数据,所有数据都由函数的调用者提供
3、使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
4、不调用不可重入函数,比如:malloc,free,因为malloc是用全局链表来管理堆的。调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。
不可重入的后果
可重入与线程安全并不等同。一般说来,可重入的函数一定是线程安全的,但反过来不一定成立。
2、线程安全函数
一般说来,一个函数被称为是线程安全的,并且仅当被多个并发线程反复调用,他会一直产生正确的结果。
确保线程安全:
要确保函数线程安全,主要需要考虑的是线程之间的共享变量。属于同一进程的怒通线程会共享进程内存空间中的全局区和堆,而私有的线程则主要包括栈和寄存器。因此,对于同一进程的不同线程来说,每个线程的局部变量都是私有的,而全局变量、局部静态变量、分配于堆的变量都是共享的。在对这些共享变量进行访问时,如果要保证线程安全,则必须通过加锁的方式。
线程不安全的后果:
线程不安全可能导致的后果是显而易见的——共享变量的值由于不同线程的访问,可能发生不可预料的变化,进而导致程序的错误,甚至崩溃。
3. 可重入函数与线程安全的区别与联系
线程安全是在多个线程的情况下引发的,而可重入函数可以在只有一个线程的情况下来说;
线程安全不一定是可重入的,而可重入函数则一定是线程安全的;
如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的;
如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果重入函数的话若锁还未释放则会产生死锁,因此不是可重入的;
如果一个函数当中的数据全是自身栈空间的,那么这个函数既是线程安全也是可重入的;
线程安全函数能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据的操作互不影响使结果是相同的