[英]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.