2009-08-21 119 views
68

我总是感到困惑。有人会解释什么Reentrant意味着在不同的情况下?你为什么要使用可重入和不可重入?什么是重入锁和概念?

说pthread(posix)锁定原语,它们是否可重入?使用它们时应该避免哪些陷阱?

互斥体是否可重入?

回答

125

重入锁定

一个可重入锁是其中一个进程可以锁定多次权利要求而不阻塞在其自身上。在跟踪您是否已经抓住锁的情况下很有用。如果锁不可重入,您可以抓住锁,然后在再次抓住锁时阻止,从而使自己的流程陷入僵局。

一般而言,重入是代码的一个属性,它没有中央可变状态,如果代码在执行时被调用,可能会被破坏。这样的调用可以由另一个线程完成,也可以通过源自代码本身的执行路径递归地进行。

如果代码依赖于可以在执行过程中更新的共享状态,它不会重入,至少在更新可能会破坏它的情况下不会重入。

一个用例重入锁定

A(有些通用的,做作),例如,对于重入锁的应用程序可能是:

  • 你有一些计算涉及一种遍历图的算法(可能有循环)。由于循环或由于到相同节点的多条路径,遍历可能会多次访问同一节点。

  • 数据结构受到并发访问的限制,可能由于某种原因更新,可能是由另一个线程更新的。您需要能够锁定各个节点以处理由于竞争条件而导致的潜在数据损坏。出于某种原因(也许性能),您不希望全局锁定整个数据结构。

  • 您的计算无法保留您访问过哪些节点的完整信息,或者您正在使用的数据结构不允许'我以前在这里'的问题需要快速回答。

    这种情况的一个例子是Dijkstra算法的简单实现,该算法将优先级队列实现为二进制堆或使用简单链表作为队列的广度优先搜索。在这些情况下,扫描队列中现有的插入是O(N),您可能不希望在每次迭代时都这样做。

在这种情况下,跟踪您已获取的锁是非常昂贵的。假设您想要在节点级别执行锁定,则重入锁定机制可以减轻您是否曾经访问节点的需要。您可以盲目地锁定节点,也许在将它从队列中弹出后解锁。

重入互斥

一个简单的互斥是不可重入作为只有一个线程可以是在给定时间的临界区。如果你抓住互斥锁然后尝试再次抓住它,一个简单的互斥锁就没有足够的信息来告诉谁以前持有它。要递归执行此操作,您需要一种机制,其中每个线程都有一个令牌,以便您可以分辨谁已经获取了互斥锁。这使得互斥体机制更加昂贵,所以你可能不想在所有情况下都这样做。

IIRC POSIX线程API确实提供了可重入和不可重入互斥选项。

+1

虽然这种情况通常应该避免,因为它也很难避免死锁等。无论如何,线程已经够难了,而不必怀疑你是否已经锁定了。 – 2009-08-21 14:28:33

+0

+1,还要考虑锁定不可重入的情况,如果你不小心,可以阻止自己。此外,在C语言中,您没有与其他语言一样的机制来确保锁定被释放多次,因为它被收购。这可能会导致很大的问题。 – user7116 2009-08-21 14:30:50

+1

这就是昨天发生在我身上的事情:我没有将再次进入的问题纳入考虑范围,并最终调试了5个小时的僵局...... – vehomzzz 2009-08-21 14:34:55

17

重入锁让你写一个方法M这给资源A锁,然后调用M递归或从已经持有A锁码。

使用非重入锁定,您需要2个版本的M,一个锁定,一个不锁定,另外一个逻辑可以调用正确的锁定。

+1

这里的图片说明会很有帮助... – Rachel 2013-08-20 18:29:53

9

这个tutorial中的折返锁很好地被描述。

本教程中的示例远不及关于遍历图的答案。在非常简单的情况下,重入锁定非常有用。