锁定多个互斥量

26
我想知道是否可能同时锁定多个互斥量,例如:

我想知道是否可以同时锁定多个互斥体,例如:

 Mutex1.Lock();
 {
     Mutex2.Lock();
     {
          // Code locked by mutex 1 and 2.
     }
     Mutex2.Unlock();

     // Code locked by mutex 1.
 }
 Mutex1.Unlock();

这在某些情况下会非常有用。谢谢。


3
是的,这是可能的。只要小心,如果没有适当的谨慎,很容易导致死锁。 - Michael Burr
3个回答

46

std::lock 似乎是为了解决这个问题而存在的。

使用死锁避免算法锁定给定的 Lockable 对象 lock1、lock2、...、lockn,以避免死锁。这些对象通过一系列未指定的 lock、try_lock、unlock 调用进行锁定。如果 lock 或 unlock 的调用导致异常,则在重新抛出之前,对任何已锁定的对象调用 unlock。

http://zh.cppreference.com/w/cpp/thread/lock


36

C++17还提供了scoped_lock,用于锁定多个互斥对象,以RAII风格避免死锁,类似于lock_guard

#include<mutex>

std::mutex mtx1, mtx2;
void foo()
{
    std::scoped_lock lck{mtx1, mtx2};
    // proceed
}

不幸的是,目前Ubuntu默认的gcc v5.4还不支持std::scoped_lock。但可以使用Boost库的类似功能。 - mc.dev

13

如果应用程序中两个线程以相反的顺序获取锁,则每个线程都可能在等待另一个线程释放其中一个锁,因此必须保持一致的锁定顺序,否则会导致死锁。

建议使用作用域锁定和解锁工具来确保异常安全,以确保锁始终被释放(例如,std::mutexstd::lock_guard):

std::mutex mtx1;
std::mutex mtx2;

std::lock_guard<std::mutex> mtx1_lock(mtx1);
{
    std::lock_guard<std::mutex> mtx2_lock(mtx2);
    {
    }
}

如果你的编译器不支持这些C++11特性,那么boost有类似的功能在boost::mutexboost::lock_guard中。


我正在使用pthread,是否可以在不使用c++11的情况下保护我的程序免受死锁?我在.Lock()和.Unlock()函数中使用pthread_mutex_lock和unlock。 - grimgrom
@grimgrom,是的。提到lock_guard是因为它使得异常安全更容易实现,但并不是避免死锁所必需的。为了避免死锁,请确保锁总是按照相同的顺序获取,并且无论锁获取后的代码如何退出,都要_始终_释放锁。 - hmjd
这只是使处理异常更简单而已。 - hmjd
3
如果在其他地方的代码在锁定mtx1之前锁定了mtx2,并且与此代码同时锁定了mtx1然后锁定了mtx2,那么锁保护程序将无法解决死锁问题。两个线程都将永远等待另一个线程释放它们所需的锁。这就是本答案开头提到的问题,但似乎已经被RAII讨论所掩盖。死锁是多重锁定的主要问题。 - doug65536
2
这个例子是不安全的,正如doug65536已经提到的那样,除非你能保证每次锁定这些互斥量时,它们总是按相同的顺序锁定(祝你好运)。绝对安全的方法是使用std::lock,就像Pubby建议的那样。 - paercebal
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接