c++ std::thread: 这段代码肯定会死锁吗?

c++ std::thread: Is this code guaranteed to deadlock?

以下代码来自modernescpp。我知道当持有互斥锁的主线程中的 lock_guard 导致死锁。但是由于创建的线程一旦初始化就应该开始 运行 。有没有可能在第 15 行之后,第 11 行的函数 lock_guard 已经获取了 coutMutex,所以代码 运行s 没有任何问题?如果可以,什么情况下创建的线程 运行 会先吗?

#include <iostream>
#include <mutex>
#include <thread>

std::mutex coutMutex;

int main(){

  std::thread t([]{
    std::cout << "Still waiting ..." << std::endl;
    std::lock_guard<std::mutex> lockGuard(coutMutex); // Line 11
    std::cout << std::this_thread::get_id() << std::endl;
    }
  );
  // Line 15
  {
    std::lock_guard<std::mutex> lockGuard(coutMutex);
    std::cout << std::this_thread::get_id() << std::endl;
    t.join();
  }
}

所以答案将作为答案发布,而不是评论:

  • 不,此代码不保证死锁。
  • 是的,此代码很可能死锁。

特别是,主线程有可能创建从属线程,然后两者都被挂起。从那时起,由 OS 调度程序决定接下来 运行 哪个。由于主线程最近是 运行,因此很有可能它将 select 下一个从属线程 运行 (假设它试图在不存在的情况下尝试遵循一些模糊的循环调度)优先级的差异,或类似的东西让它优先安排哪个线程)。

有多种方法可以解决死锁的可能性。一种明显的可能性是将 join 移动到主线程持有互斥锁的范围之外:

#include <iostream>
#include <mutex>
#include <thread>

std::mutex coutMutex;

int main(){

  std::thread t([]{
    std::cout << "Still waiting ..." << std::endl;
    std::lock_guard<std::mutex> lockGuard(coutMutex); // Line 11
    std::cout << std::this_thread::get_id() << std::endl;
    }
  );
  // Line 15
  {
    std::lock_guard<std::mutex> lockGuard(coutMutex);
    std::cout << std::this_thread::get_id() << std::endl;
  }
  t.join();
}

我也会避免在使用 std::cout 期间锁定互斥锁。 cout 通常足够慢,这样做很可能会争用锁。通常最好的做法是(仅举一个例子)将数据格式化到缓冲区中,将缓冲区放入队列中,并让一个线程从队列中读取项目并将它们推到 cout。这样你只需要锁定足够长的时间来 add/remove 缓冲区 to/from 队列。