some time ago i was asked to make a code review of a multi-threaded code part. i found a code block like this:
std::condition_variable cv; std::mutex m; // ...a lot of code here... std::unique_lock<std::mutex> lock(m); cv.notify_one();
and commented it as a “no-no” – why to notify, while still having mutex locked?! it's obvious other thread will do something like this:
std::unique_lock<std::mutex> lock(m); cv.wait(lock, someConditionCheck); // ... now process
so it means we're waking up a thread, just so it can block on mutex, then we unlock and wake it up again, so that it can process. sounds like a waste, doesn't it?
well – it appears it does not work that way. in fact POSIX advises signaling under a lock. dunno what are implementation details here, but doing this:
m.lock(); // ... cv.notify_one(); m.unlock();
performs better than this:
m.lock() // ... m.unlock(); cv.notify_one();
which was quite surprising for me.
in fact the gain for linux system was ~3-10% (depending on the particular run and the machine test were run on), while at my friend's windows platform difference was as high as 30% (reference implementation)!
even though i still do not know why it is so, the good news is that naturally written RAII code:
std::lock_guard<std::mutex> lock(m); // ... cv.notify_one();
works as expected and is superior, when it comes to performance.