我在stackoverflow上读到了这个答案:
由于递归互斥锁具有所有权意义,因此抓取互斥锁的线程必须是释放互斥锁的同一线程。对于非递归互斥锁,没有所有权感,任何线程通常都可以释放互斥锁,无论最初哪个线程获取了互斥锁。
我对最后一句话感到困惑。一个线程可以锁定一个互斥锁,而另一个不同的线程可以解锁该互斥锁吗?我认为只有同一线程才能解锁互斥锁?还是有特定的互斥锁可以允许这样做?希望有人可以澄清。
我在stackoverflow上读到了这个答案:
由于递归互斥锁具有所有权意义,因此抓取互斥锁的线程必须是释放互斥锁的同一线程。对于非递归互斥锁,没有所有权感,任何线程通常都可以释放互斥锁,无论最初哪个线程获取了互斥锁。
我对最后一句话感到困惑。一个线程可以锁定一个互斥锁,而另一个不同的线程可以解锁该互斥锁吗?我认为只有同一线程才能解锁互斥锁?还是有特定的互斥锁可以允许这样做?希望有人可以澄清。
大多数互斥锁都是(或者至少应该是)非递归的。互斥锁是一种可以原子性地获取或释放的对象,它可以保护在多个线程之间共享的数据免受竞态条件、数据损坏和其他不良影响。
同一个线程在同一调用链中只能获取一次单个互斥锁。试图在同一线程上下文中多次获取(或持有)同一个互斥锁应被视为无效场景,并应适当处理(通常通过断言来处理,因为您正在违反代码的基本约定)。
这应该被视为代码异味或骚操作。递归互斥锁与标准互斥锁唯一的区别在于,同一线程可以多次获取递归互斥锁。
需要递归互斥锁的根本原因是缺乏所有权,没有明确的目的或类之间的界限。例如,您的代码可能调用另一个类,然后该类再次调用回您的类。起始类随后可能会尝试再次获取相同的互斥锁,为了避免崩溃,您将其实现为递归互斥锁。
这种颠倒的类层次结构可能会导致各种头疼问题,而递归互斥锁只是为更基本的架构问题提供了一个临时解决方案。
无论互斥锁类型如何,总是应该由相同的线程获取和释放同一个互斥锁。您在代码中使用的一般模式如下:
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
当你有多个可以同时获取的互斥锁(比如说,mutex 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)。
递归互斥锁是按设计针对线程的(同一线程再次锁定 = 递归,另一个线程同时锁定 = 阻塞)。常规互斥锁没有这种设计,因此它们实际上可以在不同的线程中被锁定和解锁。
我认为这涵盖了你所有的问题。直接从Linux pthreads手册中来:
如果互斥锁类型为PTHREAD_MUTEX_NORMAL,则不提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果线程试图解锁它没有锁定或解锁的互斥锁,将导致未定义行为。