我在SO上阅读this answer:
因为递归互斥锁具有所有权感,所以抓取互斥锁的线程必须与释放互斥锁的线程相同。在非递归互斥锁的情况下,没有所有权感,任何线程通常都可以释放互斥锁,无论哪个线程最初使用互斥锁。
我对最后一句话感到困惑。一个线程可以锁定一个互斥锁,另一个不同的线程可以解锁该互斥锁吗?我以为同一个线程应该是唯一能够解锁互斥锁的线程?或者是否有任何特定的互斥量允许这个?我希望有人能澄清一下。
答案 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,则尝试递归锁定互斥锁会导致未定义的行为。 尝试解锁互斥锁 如果未被调用线程锁定,则会导致未定义的行为。如果未锁定互斥锁,则尝试解锁互斥锁会导致未定义 行为。