3

我在 SO 上阅读了这个答案:

因为递归互斥体有归属感,所以抓取互斥体的线程必须是释放互斥体的同一个线程。在非递归互斥锁的情况下,没有所有权感,任何线程通常都可以释放互斥锁,无论哪个线程最初获取互斥锁。

我对最后的陈述感到困惑。一个线程可以锁定一个互斥锁,而另一个不同的线程可以解锁该互斥锁吗?我认为同一个线程应该是唯一能够解锁互斥锁的线程?或者是否有任何特定的互斥锁允许这样做?我希望有人能澄清一下。

4

3 回答 3

6

非递归互斥锁

大多数互斥锁是(或至少应该是)非递归的。互斥锁是一个可以原子地获取或释放的对象,它允许在多个线程之间共享的数据受到保护,免受竞争条件、数据损坏和其他讨厌的事情的影响。

同一个调用链中的单个线程只能获取一次互斥锁。尝试在同一个线程上下文中两次获取(或持有)同一个互斥锁,应该被认为是一个无效的场景,并且应该被适当地处理(通常通过一个 ASSERT,因为你正在破坏你的代码的基本合同)。

递归互斥锁

这应该被认为是代码异味或黑客攻击。递归互斥锁与标准互斥锁不同的唯一方式是递归互斥锁可以由同一线程多次获取。

需要递归互斥锁的根本原因是缺乏所有权,并且没有明确的目的或类之间的划分。例如,您的代码可能会调用另一个类,然后该类会回调您的类。然后,起始类可以尝试再次获取相同的互斥锁,并且因为您想避免崩溃,所以您将其实现为递归互斥锁。

这种颠倒的类层次结构可能会导致各种令人头疼的问题,而递归互斥锁只能为更基本的架构问题提供临时解决方案。


无论互斥锁类型如何,它应该始终是获取和释放相同互斥锁的同一个线程您在代码中使用的一般模式是这样的:

Thread 1

    Acquire mutex A
    // Modify or read shared data
    Release mutex A

Thread 2

    Attempt to acquire mutex A
    Block as thread 1 has mutex A
    When thread 1 has released mutex A, acquire it
    // Modify or read shared data
    Release mutex A

当您有多个可以同时获取的互斥锁(例如,互斥锁 A 和 B)时,情况会变得更加复杂。您可能会遇到这样的死锁情况:

Thread 1

    Acquire mutex A
    // Access some data...

*** Context switch to thread 2 ***

Thread 2

    Acquire mutex B
    // Access some data

*** Context switch to thread 1 ***

    Attempt to acquire mutex B
    Wait for thread 2 to release mutex B

*** Context switch to thread 2 ***

    Attempt to acquire mutex A
    Wait for thread 1 to release mutex A

*** DEADLOCK ***

现在,每个线程都在等待另一个线程释放另一个锁——这就是所谓的ABBA 死锁模式

为了防止这种情况,重要的是每个线程总是以相同的顺序获取互斥锁(例如总是 A,然后是 B)。

于 2010-02-24T03:36:23.567 回答
1

递归互斥锁在设计上是特定于线程的(再次锁定相同的线程=递归,同时锁定另一个线程=阻塞)。常规互斥锁没有这种设计,因此它们实际上可以在不同的线程中锁定和解锁。

于 2010-02-24T03:34:59.450 回答
1

我认为这涵盖了你所有的问题。直接来自 pthreads 的 linux 手册页:

如果互斥锁类型为 PTHREAD_MUTEX_NORMAL,则不应提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果线程尝试解锁它尚未锁定的互斥锁或解锁的互斥锁,则会导致未定义的行为。

如果互斥锁类型是 PTHREAD_MUTEX_ERRORCHECK,则应提供错误检查。如果线程试图重新锁定它已经锁定的互斥锁,则应返回错误。如果一个线程试图解锁一个它尚未锁定的互斥锁或一个已解锁的互斥锁,将返回一个错误。

如果互斥锁类型是 PTHREAD_MUTEX_RECURSIVE,则互斥锁应维护锁计数的概念。当线程第一次成功获取互斥锁时,锁计数应设置为 1。每次线程重新锁定此互斥体时,锁定计数应加一。每次线程解锁互斥锁时,锁计数应减一。当锁计数达到零时,互斥锁将可供其他线程获取。如果一个线程试图解锁一个它尚未锁定的互斥锁或一个已解锁的互斥锁,将返回一个错误。

如果互斥锁类型是 PTHREAD_MUTEX_DEFAULT,尝试递归锁定互斥锁会导致未定义的行为。如果调用线程未锁定互斥锁,则尝试解锁互斥锁会导致未定义的行为。如果互斥锁未锁定,则尝试解锁它会导致未定义的行为。

于 2010-02-24T03:53:36.900 回答