为什么这个双重互斥锁不会造成死锁呢?

Why does this double mutex lock not cause deadlock?

我在我的 centos 电脑上测试了 c++11 mutex。我尝试双重锁定此互斥锁以造成死锁。但是我运行它之后,一切正常,没有发生死锁。

#include <thread>
#include <mutex>
#include <iostream>
std::mutex m;
int main()
{
  m.lock();
  m.lock();
  std::cout<<"i am ok"<<std::endl;

  return 0;  
}

编译器是centos 3.10.0-327.36.3.el7中的g++ 4.8.5。x86_64:

[zzhao010@localhost shareLibPlay]$ ./3.out
i am ok

锁定一个已经被同一个线程锁定的 std::mutex 未定义的行为 因此它可能有效,也可能失败,它可能会喝光你所有的啤酒并且吐在沙发上。没有保证。

如果您调用 lock 两次,则行为未定义。
它像您期望的那样工作确实是一个有效的未定义行为。

有关详细信息,请参阅 here

一个死锁,你至少需要两个

definition, a deadlock involves at least 2 parties. This was laid down by many authors, among others Hoare 在他的开创性著作 Communicating Sequential Processes 中发表。 C++标准定义中也提醒了这一点(重点是我的):

17.3.8: Deadlock: one or more threads are unable to continue execution because each is blocked waiting for one or more of the others to satisfy some condition

Anthony Williams 在 C++ concurrency in action

中给出了更具说明性的定义

Neither thread can proceed, because each is waiting for the other to release it's mutex. This scenario is called deadlock and it's the biggest problem with having to lock two or more mutexes.

因此,根据定义,您可以不在单个进程中使用单个线程创建死锁。

不要误解标准

标准关于互斥体的说法:

30.4.1.2.1/4 [Note: A program may deadlock if the thread that owns a mutex object calls lock() on that object.]

这是一个非规范性的注释。我认为它与标准自己的定义令人尴尬地矛盾。从术语上看,锁定自己的进程处于阻塞状态。

但更重要的是,除了死锁术语问题之外,“可以”一词允许 C++ 实现的上述行为(例如,如果它不能在特定 OS 上检测冗余锁获取).但这根本不是必需的:我相信大多数主流 C++ 实现都可以正常工作,正如您自己所经历的那样。

想体验死锁吗?

如果您想体验真正的死锁,或者如果您只想了解您的 C++ 实现是否能够检测到 resource_deadlock_would_occur 错误,请看这里的一个简短示例。它可以正常运行,但很可能会造成死锁:

std::mutex m1,m2;
void foo() {
    m1.lock();
    std::cout<<"foo locked m1"<<std::endl;
    std::this_thread::sleep_for (std::chrono::seconds(1));
    m2.lock();
    m1.unlock(); 
    std::cout<<"foo locked m2 and releases m1"<<std::endl;
    m2.unlock(); 
    std::cout<<"foo is ok"<<std::endl;
}
void bar() {
    m2.lock();
    std::cout<<"bar locked m2"<<std::endl;
    std::this_thread::sleep_for (std::chrono::seconds(1));
    m1.lock();
    m2.unlock(); 
    std::cout<<"barlocked m1 and releases m2"<<std::endl;
    m1.unlock(); 
    std::cout<<"bar is ok"<<std::endl;
}
int main()
{
    std::thread t1(foo); 
    bar(); 
    t1.join(); 
    std::cout << "Everything went fine"<<std::endl; 
    return 0;  
}

Online demo

通过始终以相同的顺序锁定不同的互斥体,可以避免这种死锁。