scoped_lock 可以在读取模式下锁定 shared_mutex 吗?
Can scoped_lock lock a shared_mutex in read mode?
C++17 同时引入了 std::shared_mutex
和 std::scoped_lock
。我现在的问题是,当它作为参数传递时,scoped_lock
将始终以独占(编写器)模式锁定共享互斥锁,而不是共享(reader)模式。在我的应用程序中,我需要使用来自对象 src
的数据更新对象 dst
。我想锁定 src
共享和 dst
独占。不幸的是,如果同时调用切换 src
和 dst
的另一个更新方法,则可能会出现死锁。所以我想使用 std::scoped_lock
.
的奇特死锁避免机制
我可以使用 scoped_lock
在独占模式下同时锁定 src
和 dst
,但是这种不必要的严格锁定会在其他地方产生性能倒退。然而,似乎可以将 src
的 shared_mutex
包装成 std::shared_lock
并将其与 scoped_lock
一起使用:当 scoped_lock
在其锁定操作在 shared_lock
上调用 try_lock()
,后者实际上会在 src
的 shared_mutex
上调用 try_shared_lock()
,这就是我需要的。
所以我的代码看起来很简单:
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++ 标准库的实现代码的各种评论员所指出的:是的,使用 std::shared_mutex
包裹在 std::shared_lock()
中作为 [ 的参数之一=14=] 是安全的。
基本上,std::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
对象并锁定所有对象(或异常中止,在这种情况下它们将全部解锁)。
根据 cppreference,std::scoped_lock
是 std::lock
的包装器,具有在其析构函数中的每个 Lockable 对象上调用 unlock()
的附加功能。这里不需要添加的功能,因为 std::shared_lock lk1
和 std::unique_lock lk2
也可以用作锁守卫,当它们超出范围时解锁它们的互斥量。
编辑:各种说明
Mutex: 为共享资源添加线程安全
Lock:将 RAII(以及可能的额外功能)添加到 mutex
不同的锁让您以不同的方式锁定互斥量:
- unique_lock: 对资源的独占访问(写)
- shared_lock: 共享资源访问(同时读取)
- scoped_lock:与unique_lock相同,但功能较少
scoped_lock
是一个裸骨独占锁,构造时加锁,销毁时解锁。 unique_lock
和 shared_lock
分别是独占锁和共享锁,它们也使用它们的默认构造函数和析构函数进行锁定和解锁。但是,它们还提供额外的功能。例如,您可以try to luck them,或者您可以在它们被摧毁之前解锁它们。
所以一个典型的用例是使用 shared_lock
进行共享访问(当多个线程读取相同的资源时),并使用 unique_lock
或 scoped_lock
进行独占访问访问权限(取决于您是否需要 unique_lock
的额外功能)。
C++17 同时引入了 std::shared_mutex
和 std::scoped_lock
。我现在的问题是,当它作为参数传递时,scoped_lock
将始终以独占(编写器)模式锁定共享互斥锁,而不是共享(reader)模式。在我的应用程序中,我需要使用来自对象 src
的数据更新对象 dst
。我想锁定 src
共享和 dst
独占。不幸的是,如果同时调用切换 src
和 dst
的另一个更新方法,则可能会出现死锁。所以我想使用 std::scoped_lock
.
我可以使用 scoped_lock
在独占模式下同时锁定 src
和 dst
,但是这种不必要的严格锁定会在其他地方产生性能倒退。然而,似乎可以将 src
的 shared_mutex
包装成 std::shared_lock
并将其与 scoped_lock
一起使用:当 scoped_lock
在其锁定操作在 shared_lock
上调用 try_lock()
,后者实际上会在 src
的 shared_mutex
上调用 try_shared_lock()
,这就是我需要的。
所以我的代码看起来很简单:
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++ 标准库的实现代码的各种评论员所指出的:是的,使用 std::shared_mutex
包裹在 std::shared_lock()
中作为 [ 的参数之一=14=] 是安全的。
基本上,std::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
是 std::lock
的包装器,具有在其析构函数中的每个 Lockable 对象上调用 unlock()
的附加功能。这里不需要添加的功能,因为 std::shared_lock lk1
和 std::unique_lock lk2
也可以用作锁守卫,当它们超出范围时解锁它们的互斥量。
编辑:各种说明
Mutex: 为共享资源添加线程安全
Lock:将 RAII(以及可能的额外功能)添加到 mutex
不同的锁让您以不同的方式锁定互斥量:
- unique_lock: 对资源的独占访问(写)
- shared_lock: 共享资源访问(同时读取)
- scoped_lock:与unique_lock相同,但功能较少
scoped_lock
是一个裸骨独占锁,构造时加锁,销毁时解锁。 unique_lock
和 shared_lock
分别是独占锁和共享锁,它们也使用它们的默认构造函数和析构函数进行锁定和解锁。但是,它们还提供额外的功能。例如,您可以try to luck them,或者您可以在它们被摧毁之前解锁它们。
所以一个典型的用例是使用 shared_lock
进行共享访问(当多个线程读取相同的资源时),并使用 unique_lock
或 scoped_lock
进行独占访问访问权限(取决于您是否需要 unique_lock
的额外功能)。