7

I'm not quite sure why std::unique_lock<std::mutex> is useful over just using a normal lock. An example in the code I'm looking at is:

{//aquire lock

        std::unique_lock<std::mutex> lock(queue_mutex);

        //add task
        tasks.push_back(std::function<void()>(f));

}//release lock

why would this preferred over

queue_mutex.lock();

//add task
//...

queue_mutex.unlock();

do these snippets of code accomplish the same thing?

Syntactic Fructose
  • 18,936
  • 23
  • 91
  • 177
  • 5
    Think about an exception thrown between `lock/unlock` calls in your latter sample ... – πάντα ῥεῖ May 27 '14 at 15:56
  • The following link might be of interest to you once you have understood this question. https://stackoverflow.com/questions/20516773/stdunique-lockstdmutex-or-stdlock-guardstdmutex/20516876#20516876 – Stephan Dollberg May 27 '14 at 17:37

2 Answers2

20

[Do] these snippets of code accomplish the same thing?

No.

The first one will release the lock at the end of the block, no matter what the block is. The second will not release the lock at the end if the critical section is exited with a break, continue, return, goto, exception, or any other kind of non-local jump that I'm forgetting about.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • Beat me to it! I would also add that there's significant advantage to std::unique_lock in respect to RAII. Assume that a method inside a mutex throws before unlock() is called. The mutex could potentially never unlock. std::unique_lock is guaranteed to release even if an exception is thrown where-as std::mutex will not (since unlock must be called manually). – David Peterson May 27 '14 at 15:58
  • 5
    IOW, the first one actually *does* what most people are hoping for when they write the second. – Jerry Coffin May 27 '14 at 15:58
  • So would it be wise to always opt to use `std::unique_lock` whenever applicable in the future? – Syntactic Fructose May 27 '14 at 15:59
  • @SyntacticFructose I would say yes. It's a bit safer all in all. – David Peterson May 27 '14 at 16:00
  • @SyntacticFructose In almost all cases. There are a few (very rare) cases where you want the lock to live beyond the end of the block. (I've put the `unlock` in the deleter object of a `shared_ptr`, for example.) – James Kanze May 27 '14 at 16:59
  • 2
    @SyntacticFructose: In most cases, a simple `std::lock_guard` should actually suffice. It's generally a good idea to use the simplest possible type to achieve a certain task. – Xeo May 27 '14 at 17:35
5

The use of unique_lock offers resiliency in the face of changes and errors.

  • If you change the flow to add intermediate "jumps" (return for example)
  • If an exception is thrown
  • ...

in any case, the lock is automatically released.

On the other hand, if you attempt to do it manually, you may miss a case. And even if you don't right now, a later edit might.


Note: this is a usual idiom in C++, referred to as SBRM (Scoped Bound Resources Management) where you tie down a clean-up action to stack unwinding so you are assured that, unless crash/ungraceful exit, it is executed.

It also shows off RAII (Resources Acquisition is Initialization) since the very construction of unique_lock acquires the resource (here the mutex). Despite its name, this acronym is also colloquially used to refer to deterministic release at destruction time, which covers a broader scope than SBRM since it refers to all kind of deterministic releases, not only those based on stack unwinding.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722