这是我对引用报价的解释。我希望它既可以理解,又符合编写原始答案的人的意图。
假设您有一个需要受互斥体保护的数据结构。您可以选择如何“细化”处理处理这些对象的关键部分。这些选项还可能影响线程可能需要如何行为以同时获取多个对象的锁:
每个对象使用一个互斥锁:
struct foo {
mutex mux;
// important data fields...
};
这样做的好处是处理不同对象的线程不会发生争用。如果单个线程需要在为多个对象持有锁的同时处理多个对象(我认为这就是“设置合并”的意思),则不需要递归互斥锁。但是,确实需要注意避免死锁。
让每个对象引用一个可以与其他对象共享的递归互斥锁:
struct foo {
recursive_mutex* pmux;
// important data fields...
};
由于两个对象实际上可能与单个互斥锁相关联,如果线程 1 尝试锁定对象 A 并且线程 2 在对象 A 和 B 共享同一个互斥锁时同时尝试锁定对象 B,则其中一个线程将阻塞,直到另一个线程释放互斥锁。由于互斥锁是递归的,单个线程可能会锁定多个对象,即使它们共享同一个互斥锁。请注意,关于死锁仍然有同样的警告。
与第一种方案相比,此方案的(可能)优势在于,如果一个线程必须同时锁定多个对象,则该集合中的某些对象很有可能共享一个互斥锁。一旦线程锁定了一个对象,理论上在尝试锁定下一个对象时阻塞的可能性就会降低。但是,我认为在实践中可能很难证明您将获得这种好处,除非您能够真正描述线程的锁定行为以及它们将锁定的对象集(并设置互斥共享以反映模型)。
该引用中的最后一项实质上是指在上述场景中使用非递归锁。在这种情况下,您需要防止线程尝试“重新锁定”互斥锁(当然,这不能使用非递归互斥锁来完成),因此线程必须以某种方式比较它即将执行的锁获取它已经获取的锁以确定它是否应该或不应该获取该对象上的锁。如果涉及多个对象,这可能会成为一个复杂的场景,以确保线程获得了正确的锁集。
struct foo {
mutex* pmux; // pointer to (possibly shared) non-recursive mutex
// important data fields...
};
// a set of objects a thread needs to work on in a critical section
// the objects possibly share non-recursive mutexes
struct foo* pA;
struct foo* pB;
struct foo* pC;
// acquire the necessary locks on all three objects:
mutex_lock( pA->pmux);
if (pB->pmux != pA->pmux) mutex_lock( pB->pmux);
if ((pC->pmux != pA->pmux) && (pC->pmux != pB->p-mux)) mutex_lock( pC->pmux);
// releasing the set of mutexes is similar
与其手动获取内联互斥锁,不如将它们传递给一个函数来管理确保忽略任何重复项的复杂性。与之前的方案一样,仍然需要解决避免死锁的问题。