首页 > 解决方案 > scoped_lock 可以在读取模式下锁定 shared_mutex 吗?

问题描述

C++17 引入了std::shared_mutexstd::scoped_lock. scoped_lock我现在的问题是,当它作为参数传递时,它似乎总是将共享互斥锁锁定在独占(写入器)模式,而不是在共享(读取器)模式下。在我的应用程序中,我需要dst使用来自 object 的数据更新 object src。我想锁定src共享和dst独占。src不幸的是,如果同时使用和dst切换调用另一个更新方法,这有可能导致死锁。所以我想使用std::scoped_lock.

我可以scoped_lock同时锁定srcdst独占模式,但是这种不必要的严格锁定会在其他地方产生性能回退。但是,似乎可以将src's包装shared_mutex成 astd::shared_lock并将其与 : 一起使用scoped_lock:当scoped_lock在其锁定操作期间调用try_lock()shared_lock,后者实际上会调用try_shared_lock()' srcs shared_mutex,这就是我所需要的。

所以我的代码看起来很简单:

struct data {
    mutable std::shared_mutex mutex;
    // actual data follows
};

void update(const data& src, data& dst)
{
    std::shared_lock slock(src.mutex, std::defer_lock);
    std::scoped_lock lockall(slock, dst.mutex);
    // now can safely update dst with src???
}

在另一个(避免死锁)锁卫中使用这样的(共享)锁卫是否安全?

标签: c++deadlockscoped-lock

解决方案


正如阅读过 C++ 标准库的实现代码的各种评论员所指出的那样:是的,使用std::shared_mutex包裹在 a中的 astd::shared_lock()作为参数之一std::scoped_lock()是安全的。

基本上,astd::shared_lock将所有调用转发到lock()互斥lock_shared()体上。

std::shared_lock::lock -----------> mutex()->lock_shared(). // same for try_lock etc..

另一种可能的解决方案

std::shared_lock lk1(src.mutex, std::defer_lock);
std::unique_lock lk2(dst.mutex, std::defer_lock);
std::lock(lk1, lk2);

std::lock是一个函数,它接受任意数量的Lockable对象并锁定所有对象(或异常中止,在这种情况下它们都将被解锁)。

std::scoped_lock根据cppreference是一个包装器std::lock,增加了在其析构函数中调用unlock()每个 Lockable 对象的功能。这里不需要添加的功能,因为std::shared_lock lk1std::unique_lock lk2也可以用作锁守卫,当它们超出范围时解锁它们的互斥锁。

编辑:各种澄清


推荐阅读