首页 > 代码库 > 可重入(Reentrancy)VS线程安全(Thread safety)

可重入(Reentrancy)VS线程安全(Thread safety)

        在Wiki上,可重入的定义如下(详细链接)

    In computing, a computer program or subroutine is called reentrant if it can be interrupted in the middle of its execution and then safely called again ("re-entered") before its previous invocations complete execution. The interruption could be caused by an internal action such as a jump or call, or by an external action such as a hardware interrupt or signal. Once the reentered invocation completes, the previous invocations will resume correct execution.

        翻译过来,就是说在函数执行的过程中,如果发生了中断被迫执行其它动作,当中断执行完成后(中断执行过程中可能会再次调用该函数),再次重新返回函数的时候,能够恢复正确的执行过程。

        显然,定义里面并没有强调必须是多线程,而且wiki上也进一步说明了一个事实

    This definition originates from single-threaded programming environments where the flow of control could be interrupted by a hardware interrupt and transferred to an interrupt service routine (ISR). 

        简单翻译,可重入的定义是根据单线程,并且控制流可以被硬件中断打断的环境的。因此一般情况下并没有涉及和考虑到多线程的情况。

        我们再来看看线程安全的wiki定义(详细链接)

    Thread safety is a computer programming concept applicable in the context of multi-threaded programs. A piece of code is thread-safe if it only manipulates shared data structures in a manner that guarantees safe execution by multiple threads at the same time. There are various strategies for making thread-safe data structures.

        简单翻译,线程安全就是保证多线程同时执行函数时,能够以保证安全执行的情况下操作共享数据。这里强调了执行的上下文必须为多线程环境。
  
        从以上两个概念的定义我们可以看到,可重入并不一定是线程安全,线程安全也并不一定是可重入的。在这里,可以举两个例子来说明

        首先,是一个可重入但不是线程安全的例子。以下代码段里,把局部变量s保留了全局变量t的状态,能够在中断发生之后恢复原来执行状态,符合可重入的定义,但很明显,由于无法确保全局数据的一致性(没加锁),因此不是线程安全

int t;
 
void swap(int *x, int *y)
{
    int s;
 
    s = t; // save global variable
    t = *x;
    *x = *y;
 
    // hardware interrupt might invoke isr() here!
    *y = t;
    t = s; // restore global variable
}
 
void isr()
{
    int x = 1, y = 2;
    swap(&x, &y);
}

        然后是一个线程安全但不是可重入的例子。由于对静态变量counter的访问之前都互斥锁进行同步,因此是线程安全的。但如果获取了互斥锁,没有释放锁的过程里,发生了中断,这时候如果在中断里再次调用函数,则会发生死锁。因此不是可重入的。

#include <pthread.h>
 
int increment_counter ()
{
	static int counter = 0;
	static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
	pthread_mutex_lock(&mutex);
 
	// only allow one thread to increment at a time
	++counter;
	// store value before any other threads increment it further
	int result = counter;	
 
	pthread_mutex_unlock(&mutex);
 
	return result;
}

        由以上两个例子里,更加清晰地说明了两者之间并不一定相互决定的关系。一般来说,实现可重入要注意在函数执行过程中,如果要保存执行进度的状态,要考虑把状态保存到局部变量(栈),TLS,不能保存在全局或者静态变量,另外还要注意不能调用其它不可重入的函数。

       最后,经过在网络上的一些调查搜索,发现了部分人对可重入有另外一种理解。简单来说,就是可重入也包括多线程的情况,也就是说当单一线程在函数执行过程中,会保证当另外线程执行的正确性。也就是可重入包括了对单线程的重入和多线程的重入,显然后者有更强的限定性,同时也确保了线程安全。

可重入(Reentrancy)VS线程安全(Thread safety)