簡體   English   中英

死鎖使用std :: mutex來保護多線程中的cout

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM