Poperton
Poperton

Reputation: 1766

How std::condition_variable and std::mutex works exactly?

I'm reading this std::condition_variable example:

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
 
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
 
void worker_thread()
{
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{return ready;});
 
    // after the wait, we own the lock.
    std::cout << "Worker thread is processing data\n";
    data += " after processing";
 
    // Send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";
 
    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}
 
int main()
{
    std::thread worker(worker_thread);
 
    data = "Example data";
    // send data to the worker thread
    {
        std::lock_guard<std::mutex> lk(m);
        ready = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();
 
    // wait for the worker
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return processed;});
    }
    std::cout << "Back in main(), data = " << data << '\n';
 
    worker.join();
}

Let's focus on the beggining:

std::thread worker(worker_thread);

here, we start the worked, which will lock the mutex immediately:

std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});

Then, we lock the mutex on main to change the value of ready

{
    std::lock_guard<std::mutex> lk(m);
    ready = true;
    std::cout << "main() signals data ready for processing\n";
}

but how is it possible that we even arrive at the line ready=true? The mutext m is blocked from the worked thread, so the line std::lock_guard<std::mutex> lk(m); will wait until the mutex m is unlocked. As I understand, when a mutex is locked and we try to lock it, we'll wait until it gets unlocked. However, it'll never get unlocked because the worked thread is waiting, therefore not releasing it.

Upvotes: 2

Views: 1451

Answers (2)

user3091673
user3091673

Reputation:

Here's another example that will give you the gist of the mechanism.

#include <cstdio>                                                                                                                                                                                                   
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>

std::mutex mtx;
std::condition_variable cv;
bool signal = 0;

void A()
{
  while (1)
  {
    {
      std::unique_lock<std::mutex> lock(mtx);
      cv.wait(lock, [](){return signal;});
      signal = 0;
    }

    printf("Lock release from th A\n");
  }
}

int main()
{
  std::thread th(A);

  for (unsigned i = 0; i < 100; i++)
  {
    {
      std::lock_guard<std::mutex> lock(mtx);
      signal = 1;
    }

    cv.notify_one();
    printf("Sending signal %i\n", i);
    std::this_thread::sleep_for(std::chrono::seconds(5));
  }

  th.join();
  return 0;
}

Upvotes: 0

asmmo
asmmo

Reputation: 7100

In the link you have attached, note that the following

The wait operations atomically release the mutex and suspend the execution of the thread.

Hence the line

cv.wait(lk, []{return ready;});

releases the mutex and suspends the execution of the thread until the condition variable is notified, a timeout expires (with wit_for()), or a spurious wake-up occurs, hence the thread is awakened, and the mutex is atomically reacquired

Upvotes: 0

Related Questions