可重入(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,不能保存在全局或者静态变量,另外还要注意不能调用其它不可重入的函数。

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

时间: 2024-10-06 16:25:11

可重入(Reentrancy)VS线程安全(Thread safety)的相关文章

可重入函数与线程安全的区别和联系

1.可重入函数 可重入函数即表示可以被多个执行流重复进入,意味着只使用自己栈上的变量,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰. 一个不可重入函数的例子: int global; int fun( int a ) { int temp; global = a; temp = gloabl*2; return temp; } global是一个全局变量,若进程a运行这段代码传入的参数是2,预期的结果是4:进程b也运行这段代码,传入的参数是3,由于操作系统的进程调

可重入与线程安全(大多数Qt类是可重入,非线程安全的)

可重入与线程安全 在Qt文档中,术语“可重入”与“线程安全”被用来说明一个函数如何用于多线程程序.假如一个类的任何函数在此类的多个不同的实例上,可以被多个线程同时调用,那么这个类被称为是“可重入”的.假如不同的线程作用在同一个实例上仍可以正常工作,那么称之为“线程安全”的. 大多数c++类天生就是可重入的,因为它们典型地仅仅引用成员数据.任何线程可以在类的一个实例上调用这样的成员函数,只要没有别的线程在同一个实例上调用这个成员函数.举例来讲,下面的Counter 类是可重入的: class Co

可重入函数 与线程安全的区别与联系

线程安全:多个线程访问同一个区域的时候其最终结果是可预期的,并不会因为产生冲突或者异常中断再次恢复而使结果不可预期 1.重入:函数被不同的控制流程调用,有可能在第一次调用还没有返回的时候就再次进入该函数这称之为 重入: 2.不可重入函数:当访问一个全局的变量或者参数的时候,有可能因为重入造成错乱,像这样的函数称之为不可重入函数 如果一个函数符合以下条件之一则是不可重入的 (1)调用了malloc/free,因为malloc是用全局链表来管理堆的 (2)调用了标准I/O卡函数,biaozhun I

可重入函数与线程安全问题

线程安全函数 确保线程安全:        要确保函数线程安全,主要需要考虑的是线程之间的共享变量.属于同一进程的不同线程会共享进程内存空间中的全局区和堆,而私有的线程空间则主要包括栈和寄存器.因此,对于同一进程的不同线程来说,每个线程的局部变量都是私有的,而全局变量.局部静态变量.分配于堆的变量都是共享的.在对这些共享变量进行访问时,如果要保证线程安全,则必须通过加锁的方式. 线程不安全的后果:        线程不安全可能导致的后果是显而易见的--共享变量的值由于不同线程的访问,可能发生不可

可重入函数与线程安全的区别与联系

1.可重入函数 1)举例说明: main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换到sighandler函数sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续往下执行,先前做第一步之后被打断,现在继续做完第二步.结果

可重入函数和线程安全

一.可重入函数 可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;,局部变量可重入函数: 不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的. 如上图: main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的 时候,因为硬

Linux可重入函数和线程安全的区别与联系(转)

*****可重入函数 函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入. 当程序运行到某一个函数的时候,可能因为硬件中断或者异常而使得在用户正在执行的代码暂时终端转而进入你内核,这个时候如有一个信号需要被处理,而处理的这个信号的时候又会重新调用刚才中断的函数,如果函数内部有一个全局变量需要被操作,那么,当信号处理完成之后重新返回用户态恢复中断函数的上下文再次继续执行的时候,对同一个全局变量的操作结果可能就会发生改变而并不如我们预期的那样,这样的函数被称为不可重入

可重入函数与线程安全

线程安全: 假如在一个函数中它是这么写的,在一个全局链表上存放数据,在单线程模式下,我们先new一个新的节点然后让head->next指向这个节点,这种场景在多线程场景下会是这样的过程,线程一new了一个节点,然后cpu转去执行线程二,线程二new一个节点后head->next指向线程二,然后执行线程一,线程一的head->next也指向它刚刚new出来的节点,这就导致一个head指向了两个节点,这也就是线程安全的问题. 导致线程安全问题需要满足下面两个条件: 1>:一定是发生在多

函数的可重入性、线程安全函数、异步信号安全函数

重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰.,常见的情况是,程序执行到某个函数foo()时,收到信号,于是暂停目前正在执行的函数,转到信号处理函数,而这个信号处理函数的执行过程中,又恰恰也会进入到刚刚执行的函数foo(),这样便发生了所谓的重入.此时如果foo()能够正确的运行,而且处理完