55

From this question I understand that std::scoped_lock is "a strictly superior version of std::lock_guard".

From this question I understand that "std::lock_guard and std::unique_lock are the same" except that std::unique_lock has some extra features (eg. try_lock) at the cost of some additional overhead.

How does std::scoped_lock compare with std::unique_lock?

Some related questions that I am hoping to get answered by this question.

  1. What are the differences between std::scoped_lock and std::unique_lock?
  2. In what situations should you use std::scoped_lock instead of std::unique_lock?
  3. In what situations should you use std::unique_lock instead of std::scoped_lock?
  4. Why does std::scoped_lock not implement some of the additional features of std::unique_lock?

1 Answer 1

45

The two objects are for different purposes. scoped_lock is for the simple case of wanting to lock some number of mutex objects in a deadlock-free way. Locking a single mutex is just a special case of locking multiple ones. The object is completely immobile, and it's very simple.

unique_lock provides a number of features, few of which are especially applicable when simultaneously locking multiple mutexes.

  • Deferred locking. Deferring would have to be all or nothing; you either defer locking all the mutexes or none of them. It's not clear why you would want to defer locking a series of mutexes, since you would have to relinquish any locks that succeeded if any of them failed.

  • Timeout locks. If you want a timeout of 100ms, does that mean that locking all of the mutexes should take no more than 100ms? That is, if the first 3 lock immediately, but the next one takes 75ms, should it be considered a timeout if the fifth takes 30ms?

  • Adoption of mutexes. The whole point of locking multiple mutexes in a single operation is to be able to avoid deadlocks. This is done by locking the mutexes in an order that is globally consistent. That is, any place where you lock those mutex objects with std::lock equivalent calls will lock them in the same order, no matter what.

    If one of the mutexes has already been locked (and thus the lock should be adopted), then it was locked outside of std::lock, and thus you have no guarantee that it was locked in the globally consistent order. And that ignores the difficulty of specifying which mutexes to adopt and which ones to lock.

  • Transfer of ownership (being moveable). This is a dubious prospect for multiple mutexes for similar reasons as adopting locks. The guarantees against deadlocks only work if a single call to std::lock or equivalent locks all of the mutexes of interest. If you're moving ownership of these scoped_locks around, it becomes very easy to be at a point in code where you have multiple scoped_locks in the same scope, when you could have locked all of them in one go. This courts the very kind of deadlock that std::lock was created to avoid.

Note that std::lock (the basis of scoped_lock's functionality) doesn't even try to provide any of these features.

Could there be a specialization of scoped_lock which took only one mutex type that offered the behavior of unique_lock? Sure. But that would violate the purpose of scoped_lock, which was to be a deadlock-safe locker for multiple mutexes. It only obsoleted lock_guard by accident, since it had the identical interface in the case of a single mutex.

Besides, having template specializations with vastly different interfaces and capabilities doesn't usually work out well. See vector<bool> as an example.

6
  • 1
    @lachy: If something can move, it is said to be "mobile". Therefore, something is "immobile" if it cannot move. Commented Oct 21, 2019 at 13:20
  • 9
    I think the more standard nomenclature is "moveable," and this is the name of the concept going forward: moveable<T>, though mobile/immobile is cute, but maybe less clear. Commented Feb 18, 2020 at 9:02
  • 2
    Is there any reason to prefer scoped_lock over unique_lock? Commented Mar 20, 2021 at 7:05
  • 5
    @hddh: It should never be a question which one to use: if you're locking multiple mutexes, you use scoped_lock; if you're locking a single mutex, use unique_lock. Commented Mar 20, 2021 at 13:21
  • 2
    fwiw, this answer makes some good points to explain why lock_guard is not deprectated Commented Jun 7, 2021 at 6:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.