5

Below is some code example about C++ condition variable from CPPConference.com:

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();
}

I do not quite understand the part at the end where the lock is released before the other thread is notified.

  1. Does it work if the code put cv.notify_one() before lk.unlock()?
  2. Why is it a better practice to put cv.notify_one() after lk.unlock()? According to this page, "The notifying thread does not need to hold the lock on the same mutex as the one held by the waiting thread(s); in fact doing so is a pessimization, since the notified thread would immediately block again, waiting for the notifying thread to release the lock." However, in my opinion, putting the notify_one before unlock causes the operations of notify->unlock->the other thread acquires the lock, wile putting the notify_one after unlock causes the operations of unlock->notify->the other thread acquires the lock. What is the difference here?
DiveIntoML
  • 2,347
  • 2
  • 20
  • 36

1 Answers1

7
  1. yes

  2. The link is correct that if the thread notifying has the lock, then the thread being notified must block until the notifying thread releases the lock. In a multicore processor this is an unnecessary delay.

Your comparison is flawed because it is missing details. There are two threads involved, both running at the same time, which your comparison omits.

putting the notify_one before unlock:

notifying thread: notify -> eventually release lock
notified thread: awaken -> attempt to acquire lock and fail -> block until lock available -> acquire lock after notifying thread releases it

putting the notify_one after unlock:

notifying thread: notify 
notified thread: awaken -> attempt to acquire lock and succeed
Sean F
  • 4,344
  • 16
  • 30
  • Can you elaborate on why the second case is better? Is it because the additional BLOCK/UNBLOCK step in the notify-then-unlock case very expensive? – DiveIntoML Mar 14 '19 at 16:09
  • the second case is better because this does not happen: attempt to acquire lock and fail -> block until lock available. Yes, calls like these are expensive, but even if they were not, why would you have your code doing things that are completely and totally unnecessary? – Sean F Mar 15 '19 at 16:24
  • 1
    Can you elaborate on what you mean by "In a multicore processor this is an unnecessary delay?" It kind of sounded to me like you're saying that it is unnecessary for the thread being notified to block until the notifying thread releases the lock if the processor is a multicore processor. Sorry if I am misunderstanding. – David Oct 25 '19 at 17:25
  • 2
    No. I am saying that anytime you hold a lock longer than is necessary, then threads waiting for the lock are waiting longer than necessary. In a single core processor, that is not so bad because only one thread can run at one time anyway. So the overall performance remains unchanged, you just change what runs when. But when you can run many threads at a time, as in a multicore, you lower performance when you have excessive locking. – Sean F Oct 25 '19 at 23:59