非递归互斥锁所有权

时间:2010-02-24 03:28:02

标签: c++ multithreading mutex recursive-mutex

我在SO上阅读this answer

  

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

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

3 个答案:

答案 0 :(得分: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)。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

  

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

     

如果互斥锁类型为PTHREAD_MUTEX_ERRORCHECK,则应提供错误检查。如果一个线程试图重新锁定它已经存在的互斥锁          锁定,应返回错误。 如果某个线程尝试解锁未锁定的互斥锁或解锁的互斥锁,则应出现错误          返回。

     

如果互斥锁类型为PTHREAD_MUTEX_RECURSIVE,则互斥锁应保持锁定计数的概念。当一个线程成功获取一个          互斥锁是第一次,锁定计数应设置为1。每次线程重新锁定此互斥锁时,锁定计数应递增1。          每次线程解锁互斥锁时,锁定计数应减1。当锁定计数达到零时,互斥锁将变为          可供其他线程获取。 如果某个线程尝试解锁未锁定的互斥锁或已解锁的互斥锁,则应出现错误          被退回。

     

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