2008-12-09 68 views

回答

21

重入代码在单点中没有状态。您可以在代码执行过程中调用代码。如果代码使用全局状态,则可以设想一次调用覆盖全局状态,从而中断另一次调用中的计算。

线程安全代码是没有竞争条件或其他并发问题的代码。竞争条件是两个线程执行某些操作会影响计算的顺序。典型的并发问题是对共享数据结构的更改可能部分完成并保持不一致状态。为了避免这种情况,您必须使用并发控制机制(例如互斥信号量),以确保在操作完成之前没有其他人可以访问数据结构。

例如,如果一段代码通过互斥体进行外部保护,但它仍具有全局数据结构,并且在整个调用过程中状态必须一致,则该代码段可以不可重入,但是线程安全。在这种情况下,同一个线程可以启动一个回调过程,同时仍受外部粗粒互斥体的保护。如果回调发生在非重入过程中,则调用可能会使数据结构处于可能打破主叫方观点计算的状态。

如果代码片段可以对共享(可共享)数据结构进行非原子更改(可在更新过程中被中断而离开数据结构),则它可以是可重入的但非线程安全的处于inc inctent状态。在这种情况下,访问数据结构的另一个线程可能会受到半变更数据结构的影响,并且可能会崩溃或执行破坏数据的操作。

+3

你的第二个例子对我来说听起来并不重要。如果改变可以被中断而留下不一致的状态,并且在该点发生信号,并且该信号的处理程序调用该功能,则通常情况会变得繁荣。这是一个重入问题,而不是线程安全问题。 – 2008-12-09 12:22:29

8

那篇文章说:

“的功能可以是可重入,线程安全的,这两者或两者都不是。”

它还说:

“不可重入函数是线程安全的”。

我可以看到这可能会导致混乱。它们表示不需要重入的标准函数也不需要是线程安全的,这对POSIX库iirc来说是正确的(POSIX声明它也适用于ANSI/ISO库,ISO有没有线程的概念,因此也没有线程安全的概念)。换句话说,“如果一个函数说它是不可重入的,那么它就是说它也是线程不安全的”。这不是一个合乎逻辑的必要性,这只是一个惯例。

这是一些线程安全的伪代码(好吧,由于锁定反转,有很多机会回调创建死锁,但让我们假设文档包含足够的信息供用户避免),但不可重入。它应该递增全局计数器,并执行回调:

take_global_lock(); 
int i = get_global_counter(); 
do_callback(i); 
set_global_counter(i+1); 
release_global_lock(); 

如果回调再次调用该程序,造成另一个回调,那么回调的两个水平将得到相同的参数(这可能是好的,取决于API),但计数器只会增加一次(这几乎肯定不是你想要的API,所以它将不得不被禁止)。

这是假设锁是递归的,当然。如果锁是非递归的,那么代码当然是不可重入的,因为第二次取锁是行不通的。

下面是一些伪代码,这是“弱重入”,但不是线程安全的:

int i = get_global_counter(); 
do_callback(i); 
set_global_counter(get_global_counter()+1); 

现在它的罚款来调用回调函数,但它不是安全的同时调用该函数来自不同的线程。从信号处理程序中调用它也是不安全的,因为如果信号发生在正确的时间,信号处理程序的再次入侵也可能破坏计数。因此,通过正确的定义,代码是不可重入的。这里有一些代码可以说是完全可重入的(除了我认为标准区分了可重入信号和'不可被信号中断',而且我不确定它会在哪里下降),但仍然不是线程化的,安全:

int i = get_global_counter(); 
do_callback(i); 
disable_signals(); // and any other kind of interrupts on your system 
set_global_counter(get_global_counter()+1); 
restore_signal_state(); 

在单线程应用程序中,这很好,假设操作系统支持禁用所有需要禁用的内容。它可以防止在临界点发生重新入侵。根据信号被禁用的方式,从信号处理程序调用可能是安全的,尽管在这个特定的例子中仍然存在传递给回调的参数对于不同的调用相同的问题。不过,它仍然可能会出现多线程错误。因为(非正式地)由于线程被调度程序中断而可能出错的任何事情,并且从另一个线程再次调用的函数也可以是非线程安全的,因此非线程安全通常意味着不可重入线程如果线程被信号中断,并且信号处理程序再次调用该函数,则会出错。但是,然后用于防止信号(禁用它们)的“修复”与用于防止并发的“修复”(通常为锁定)不同。这是最好的经验法则。

请注意,我在此暗示了全局变量,但如果函数将参数指向计数器和锁,则会应用完全相同的考虑事项。只是在使用相同参数调用各种情况时,线程不安全或不可重入,而不是完全被调用。