1.关于可重入函数
当捕捉到信号时,不论进程的主控制流程当前执行到哪,都会先跳到信号处理函数中执行,从信号处理函数返回后再继续执行主控制流程。信号处理函数是一个单独的控制流程,因为它和主控制流程是异步的,二者不存在调用和被调用的关系,并且使用不同的堆栈空间。引入了信号处理函数使得整个进程具有多个控制流程,如果这些控制流程访问相同的全局资源(全局变量、硬件资源等),就有可能出现冲突。
可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。
可重入函数也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。如果确实需要访问全局变量(包括static),一定要注意实施互斥手段。可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。
2.线程安全
一个函数被称为线程安全的(thread-safe),当且仅当被多个并发进程反复调用时,它会一直产生正确的结果。
比如一个 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个,而 实际元素是在0位置,造成丢失元素,而且Size 等于 2。这就是“线程不安全”了。
3.两者的区别和联系
可重入函数是线程安全函数的一种,其特点在于它们被多个线程调用时,不会引用任何共享数据,也就是不引用静态或全局变量。
可重入函数通常要比不可重入的线程安全函数效率高一些,因为它们不需要同步操作。更进一步说,将第2类线程不安全函数转化为线程安全函数的唯一方法就是重写它,使之可重入。
下面一个函数就是线程安全的但却不可重入的
static int *sharedID;
int* threadsafe_getID( char* name )
{
int *unsharedID;
P( &mutex );
sharedID = notThreadsafe_getID( name );
unsharedID = sharedID; V( & mutex);
return unsharedID;
}
上面的函数通过P、V操作封装得到一个线程安全函数,却不是可重入函数。 malloc()本身是线程安全的,但不是可重入的函数 既然是线程安全的,也就是有了互斥机制,就像普通函数增加了互斥信号量后变成线程安全函数一样,不必担心多线程调用会带来什么问题
本文出自 “输出菱形图案” 博客,请务必保留此出处http://10541571.blog.51cto.com/10531571/1770555
原文地址:http://10541571.blog.51cto.com/10531571/1770555