This is not an answer. It's just an illustration. I turned your one example into three different examples that all achieve the same result. I hope it may help you to better understand what unique_lock does.
The first way doesn't use unique_lock at all. It only uses the mutex. This is the old-school way—the way we used to do things before RAII was discovered.
std::mutex m; { ... while (...) { do_work_outside_critical_section(); m.lock(); // explicitly put a "lock" back on the mutex. do_work_inside_critical_section(); m.unlock(); // explicitly remove the "lock." } }
The old-school way is risky because if do_work_inside_critical_section() throws an exception, it will leave the mutex in a locked state, and any thread that tries to lock it again probably will hang forever.
The second way uses unique_lock, which is an embodiment of RAII.
The RAII pattern ensures that there's no way out of this code block that leaves a lock on mutex m. The unique_lock destructor always will be called, no matter what, and the destructor removes the lock.
std::mutex m; { ... while (...) { do_work_outside_critical_section(); std::unique_lock<std::mutex> lk(m); // constructor puts a "lock" on the mutex. do_work_inside_critical_section(); } // destructor implicitly removes the "lock." }
Notice that in this version, a unique_lock is constructed and destructed every time around the loop. That might sound costly, but it really isn't. unique_lock is meant to be used in this way.
The last way is what you did in your example. It only creates and destroys the unique_lock one time, but then it repeatedly locks and unlocks it within the loop. This works, but it's more code lines than the version above, which makes it a little bit harder to read and understand.
std::mutex m; { ... std::unique_lock<std::mutex> lk(m); // constructor puts a "lock" on the mutex. while (...) { lk.unlock(); // explicitly remove the "lock" from the mutex. do_work_outside_critical_section(); lk.lock(); // explicitly put a "lock" back on the mutex. do_work_inside_critical_section(); } } // destructor implicitly removes the "lock."