[英]Deadlock using std::mutex to protect cout in multiple threads
在多个线程中使用cout可能会导致交错输出。
所以我试图用互斥锁来保护cout。
以下代码使用std :: async启动10个后台线程。 线程启动时,会打印“Started thread ...”。 主线程按照创建顺序迭代后台线程的未来,并在相应的线程完成时打印出“Done thread ...”。
输出正确同步,但在某些线程启动后有些线程已经完成(参见下面的输出),就会出现死锁。 剩下所有后台线程,主线程正在等待互斥锁。
僵局的原因是什么?
当保留打印功能或for循环的一次迭代结束时,lock_guard应解锁互斥锁,以便其中一个等待线程能够继续。
为什么所有线程都挨饿?
码
#include <future>
#include <iostream>
#include <vector>
using namespace std;
std::mutex mtx; // mutex for critical section
int print_start(int i) {
lock_guard<mutex> g(mtx);
cout << "Started thread" << i << "(" << this_thread::get_id() << ") " << endl;
return i;
}
int main() {
vector<future<int>> futures;
for (int i = 0; i < 10; ++i) {
futures.push_back(async(print_start, i));
}
//retrieve and print the value stored in the future
for (auto &f : futures) {
lock_guard<mutex> g(mtx);
cout << "Done thread" << f.get() << "(" << this_thread::get_id() << ")" << endl;
}
cin.get();
return 0;
}
产量
Started thread0(352)
Started thread1(14944)
Started thread2(6404)
Started thread3(16884)
Done thread0(16024)
Done thread1(16024)
Done thread2(16024)
Done thread3(16024)
你的问题在于使用future::get
:
当共享状态准备就绪时,返回存储在共享状态中的值(或抛出其异常)。
如果共享状态尚未就绪(即,提供程序尚未设置其值或异常),则该函数会阻塞调用线程并等待它准备就绪。
http://www.cplusplus.com/reference/future/future/get/
因此,如果未来的线程尚未运行,则该函数将阻塞,直到该线程结束。 但是,在调用future::get
之前,您将future::get
互斥锁的所有权,因此您正在等待的任何线程都无法为自己获取互斥锁。
这应该可以解决您的死锁问题:
int value = f.get();
lock_guard<mutex> g(mtx);
cout << "Done thread" << value << "(" << this_thread::get_id() << ")" << endl;
您锁定互斥锁,然后等待其中一个期货,这反过来需要锁定互斥锁本身。 简单规则:不要等待锁定的互斥锁。
BTW:锁定输出流不是很有效,因为它甚至无法控制的代码很容易被绕过。 不是使用那些全局变量,而是为需要输出内容的代码(依赖注入)提供流,然后以线程安全的方式从该流中收集数据。 或者使用日志库,因为这可能是你想要做的。
从源头上发现原因很好。 然而,通常情况下,错误可能不容易找到。 原因可能也不同。 幸运的是,在死锁的情况下,您可以使用调试器来调查它。
我编译并运行了你的例子,然后在用gdb(gcc 4.9.2 / Linux)附加它之后,有一个回溯(跳过嘈杂的实现细节):
#0 __lll_lock_wait ()
...
#5 0x0000000000403140 in std::lock_guard<std::mutex>::lock_guard (
this=0x7ffe74903320, __m=...) at /usr/include/c++/4.9/mutex:377
#6 0x0000000000402147 in print_start (i=0) at so_deadlock.cc:9
...
#23 0x0000000000409e69 in ....::_M_complete_async() (this=0xdd4020)
at /usr/include/c++/4.9/future:1498
#24 0x0000000000402af2 in std::__future_base::_State_baseV2::wait (
this=0xdd4020) at /usr/include/c++/4.9/future:321
#25 0x0000000000404713 in std::__basic_future<int>::_M_get_result (
this=0xdd47e0) at /usr/include/c++/4.9/future:621
#26 0x0000000000403c48 in std::future<int>::get (this=0xdd47e0)
at /usr/include/c++/4.9/future:700
#27 0x000000000040229b in main () at so_deadlock.cc:24
这就是其他答案中解释的内容 - 锁定部分中的代码(so_deadlock.cc:24)调用future :: get(),然后(通过强制结果)尝试再次获取锁定。
在其他情况下可能不是那么简单,通常有几个线程,但它就在那里。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.