c++multithreadinglockingc++17

std::lock_guard or std::scoped_lock?


C++17 introduced a new lock class called std::scoped_lock.

Judging from the documentation it looks similar to the already existing std::lock_guard class.

What's the difference and when should I use it?


Solution

  • Late answer, and mostly in response to:

    You can consider std::lock_guard deprecated.

    For the common case that one needs to lock exactly one mutex, std::lock_guard has an API that is a little safer to use than scoped_lock.

    For example:

    {
       std::scoped_lock lock;  // protect this block
       ...
    }
    

    The above snippet is likely an accidental run-time error because it compiles and then does absolutely nothing. The coder probably meant:

    {
       std::scoped_lock lock{mut};  // protect this block
       ...
    }
    

    Now it locks/unlocks mut.

    If lock_guard was used in the two examples above instead, the first example is a compile-time error instead of a run-time error, and the second example has identical functionality as the version which uses scoped_lock.

    So my advice is to use the simplest tool for the job:

    1. lock_guard if you need to lock exactly 1 mutex for an entire scope.

    2. scoped_lock if you need to lock a number of mutexes that is not exactly 1.

    3. unique_lock if you need to unlock within the scope of the block (which includes use with a condition_variable).

    This advice does not imply that scoped_lock should be redesigned to not accept 0 mutexes. There exist valid use cases where it is desirable for scoped_lock to accept variadic template parameter packs which may be empty. And the empty case should not lock anything.

    And that's why lock_guard isn't deprecated. scoped_lock and unique_lock may be a superset of functionality of lock_guard, but that fact is a double-edged sword. Sometimes it is just as important what a type won't do (default construct in this case).