Deadlock using std::mutex to protect cout in multiple threads

  • A+
Category:Languages

Using cout in multiple threads might result in interleaved output.
So I tried to protect cout with a mutex.

The following code starts 10 background threads with std::async. When a thread starts, it prints "Started thread ...". The main thread iterates over the futures of the background threads in the order in which they were created and prints out "Done thread ..." when the corresponding thread finished.

The output is synchronized correctly, but after some threads have started and some have finished (see output below), a deadlock occurres. All background threads left and the main thread are waiting for the mutex.

What is the reason for the deadlock?

When the print function is left or one iteration of the for loop ends, the lock_guard should unlock the mutex, so that one of the waiting threads would be able to proceed.

Why are all the threads left starving?

Code

#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; } 

Output

Started thread0(352) Started thread1(14944) Started thread2(6404) Started thread3(16884) Done thread0(16024) Done thread1(16024) Done thread2(16024) Done thread3(16024) 


Your problem lies in the use of future::get:

Returns the value stored in the shared state (or throws its exception) when the shared state is ready.

If the shared state is not yet ready (i.e., the provider has not yet set its value or exception), the function blocks the calling thread and waits until it is ready.

http://www.cplusplus.com/reference/future/future/get/

So if the thread behind the future didn't get to run yet, the function blocks until that thread finishes. However, you take ownership of the mutex before calling future::get, so whichever thread you're waiting for will not be able to attain the mutex for itself.

This should fix your deadlock problem:

int value = f.get(); lock_guard<mutex> g(mtx); cout << "Done thread" << value << "(" << this_thread::get_id() << ")" << endl; 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: