Search code examples
c++multithreadinglockingmutexcondition-variable

Relationship of unique lock, mutex and condition variable in c++


Hello I have the following code:

// condition_variable example
#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id (int id) {
  std::unique_lock<std::mutex> lock(mtx);
  while (!ready) cv.wait(lock);
  // ...
  std::cout << "thread " << id << '\n';
}

void go() {
  std::unique_lock<std::mutex> lock(mtx);
  ready = true;
  cv.notify_all();
}

int main ()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(print_id,i);

  std::cout << "10 threads ready to race...\n";
  //go();                       // go!

  for (auto& th : threads) th.join();
  std::cout << "Finished!" << std::endl;

  return 0;
}

If I remove the comment on the function call go() inside main, I get the following output:

10 threads ready to race...
thread 0
thread 8
thread 1
thread 2
thread 4
thread 5
thread 3
thread 6
thread 7
thread 9
Finished!

I fully understand some of the flow on why I am getting this output. However, I do not understand why I cannot print "10 threads ready to race..." if I comment out go() function call inside main. If I understand it correctly, I did not invoke notify_all() by not calling go() so threads will not wake up but why is it that the print function "10 threads ready to race..." didn't get invoked as well?

And also, kindly explain why i need to call std::unique_lock lock(mtx); inside go() function. Thank you guys!

BTW just for citation, I got this example at: http://www.cplusplus.com/reference/condition_variable/condition_variable/


Solution

  • Standard output is buffered. That means that each output operation is not separately sent to the parent process, but instead they are gathered in memory and sent in bulk. This is done to minimize the overhead of system call / inter process communication - or whatever the output requires.

    When the buffer is filled, it will be flushed, and only when it is flushed do you see any output. Apparently 10 threads ready to race... was not enough to fill the buffer.

    The buffer is flushed also when the program ends, or when standard input is accessed or if you request explicitly, using std::flush or std::endl.

    When your program waits indefinitely for other threads (in a deadlock), it will never reach the point where the buffer is filled further and eventually flushed.