std::mutex 和 std::shared_mutex 之间的区别
difference between std::mutex and std::shared_mutex
我在 C++17
中遇到了一个 std::shared_mutex
。 std::shared_mutex
到底是什么,它与 std::mutex
有何不同?
中所述
The shared_mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads. In contrast to other mutex types which facilitate exclusive access, a shared_mutex has two levels of access:
- shared - several threads can share ownership of the same mutex.
- exclusive - only one thread can own the mutex.
Shared mutexes are usually used in situations when multiple readers can access the same resource at the same time without causing data races, but only one writer can do so.
这有多种用途,但一个常见的用途是实现 Read Write Lock,您可以让多个线程读取共享数据,但在任何时候都只有一个线程专门写入。因此,当您有多个读者时,互斥量在 "shared mode" 中起作用,但是当请求写入时,它会变为 "exclusive mode".
std::shared_mutex
在数据结构(如 DNS 缓存)很少更新 的情况下尤其有用。使用 std::mutex
来保护数据结构可能过于悲观,因为它消除了读取数据结构时可能的并发性
当它没有进行修改时。多个线程可以同时拥有同一个 std::shared_mutex
上的共享锁。
Anthony Williams 书中的一个这样的例子:
class dns_cache
{
std::map<std::string,dns_entry> entries;
mutable boost::shared_mutex entry_mutex;
public:
dns_entry find_entry(std::string const& domain) const
{
boost::shared_lock<boost::shared_mutex> lk(entry_mutex);
std::map<std::string,dns_entry>::const_iterator const it = entries.find(domain);
return (it==entries.end()) ? dns_entry() : it->second;
}
void update_or_add_entry(std::string const& domain,
dns_entry const& dns_details)
{
std::lock_guard<boost::shared_mutex> lk(entry_mutex);
entries[domain] = dns_details;
}
};
这里,函数find_entry
基本上做的是Read操作,而update_or_add_entry
做的是Write操作。
所以,可以说std::shared_mutex
是一个典型的reader-writer mutex,因为它允许
两种不同的用法:由单个“编写器”线程独占访问或共享,
多个“reader”线程并发访问。
A mutex
是否已锁定。
A shared_mutex
要么被独占锁定,要么被共享锁定。
任意数量的客户端都可以共享锁定共享互斥量。
如果有人独占锁定,则其他人无法持有 任何 锁。
在windows上,这是SWRLOCK
类型——实际上,这种锁通常用于实现读写锁;许多读者允许,但写作必须是独家的。
下面是一些示例代码,用于为共享和非共享互斥锁创建两个模板包装器。在一种情况下,我们有获取不同锁的读写操作。另一方面,我们只能访问:
template<class T, class M=std::mutex>
struct mutex_guarded {
template<class F>
auto access( F&& f ) {
auto l = lock();
return std::forward<F>(f)(t);
}
template<class F>
auto access( F&& f ) const {
auto l = lock();
return std::forward<F>(f)(t);
}
mutex_guarded(mutex_guarded const&)=delete;
mutex_guarded& operator=(mutex_guarded const&)=delete;
template<class...Ts>
mutex_guarded( Ts&&...ts ):t(std::forward<Ts>(ts)...){}
mutex_guarded()=default;
protected:
mutable M m;
T t;
auto lock() { return std::unique_lock<M>(m); }
};
template<class T, class M=std::shared_mutex>
struct shared_mutex_guarded:private mutex_guarded<T, M> {
using base = mutex_guarded<T, M>;
template<class F>
auto read( F&& f ) const { return access(std::forward<F>(f)); }
template<class F>
auto write( F&& f ) { return access(std::forward<F>(f)); }
using base::base;
protected:
using base::access;
template<class F>
auto access( F&& f ) const {
auto l = lock();
return std::forward<F>(f)(this->t);
}
using base::lock;
auto lock() const { return std::shared_lock<M>(this->m); }
};
我在 C++17
中遇到了一个 std::shared_mutex
。 std::shared_mutex
到底是什么,它与 std::mutex
有何不同?
The shared_mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads. In contrast to other mutex types which facilitate exclusive access, a shared_mutex has two levels of access:
- shared - several threads can share ownership of the same mutex.
- exclusive - only one thread can own the mutex.
Shared mutexes are usually used in situations when multiple readers can access the same resource at the same time without causing data races, but only one writer can do so.
这有多种用途,但一个常见的用途是实现 Read Write Lock,您可以让多个线程读取共享数据,但在任何时候都只有一个线程专门写入。因此,当您有多个读者时,互斥量在 "shared mode" 中起作用,但是当请求写入时,它会变为 "exclusive mode".
std::shared_mutex
在数据结构(如 DNS 缓存)很少更新 的情况下尤其有用。使用 std::mutex
来保护数据结构可能过于悲观,因为它消除了读取数据结构时可能的并发性
当它没有进行修改时。多个线程可以同时拥有同一个 std::shared_mutex
上的共享锁。
Anthony Williams 书中的一个这样的例子:
class dns_cache
{
std::map<std::string,dns_entry> entries;
mutable boost::shared_mutex entry_mutex;
public:
dns_entry find_entry(std::string const& domain) const
{
boost::shared_lock<boost::shared_mutex> lk(entry_mutex);
std::map<std::string,dns_entry>::const_iterator const it = entries.find(domain);
return (it==entries.end()) ? dns_entry() : it->second;
}
void update_or_add_entry(std::string const& domain,
dns_entry const& dns_details)
{
std::lock_guard<boost::shared_mutex> lk(entry_mutex);
entries[domain] = dns_details;
}
};
这里,函数find_entry
基本上做的是Read操作,而update_or_add_entry
做的是Write操作。
所以,可以说std::shared_mutex
是一个典型的reader-writer mutex,因为它允许
两种不同的用法:由单个“编写器”线程独占访问或共享,
多个“reader”线程并发访问。
A mutex
是否已锁定。
A shared_mutex
要么被独占锁定,要么被共享锁定。
任意数量的客户端都可以共享锁定共享互斥量。
如果有人独占锁定,则其他人无法持有 任何 锁。
在windows上,这是SWRLOCK
类型——实际上,这种锁通常用于实现读写锁;许多读者允许,但写作必须是独家的。
下面是一些示例代码,用于为共享和非共享互斥锁创建两个模板包装器。在一种情况下,我们有获取不同锁的读写操作。另一方面,我们只能访问:
template<class T, class M=std::mutex>
struct mutex_guarded {
template<class F>
auto access( F&& f ) {
auto l = lock();
return std::forward<F>(f)(t);
}
template<class F>
auto access( F&& f ) const {
auto l = lock();
return std::forward<F>(f)(t);
}
mutex_guarded(mutex_guarded const&)=delete;
mutex_guarded& operator=(mutex_guarded const&)=delete;
template<class...Ts>
mutex_guarded( Ts&&...ts ):t(std::forward<Ts>(ts)...){}
mutex_guarded()=default;
protected:
mutable M m;
T t;
auto lock() { return std::unique_lock<M>(m); }
};
template<class T, class M=std::shared_mutex>
struct shared_mutex_guarded:private mutex_guarded<T, M> {
using base = mutex_guarded<T, M>;
template<class F>
auto read( F&& f ) const { return access(std::forward<F>(f)); }
template<class F>
auto write( F&& f ) { return access(std::forward<F>(f)); }
using base::base;
protected:
using base::access;
template<class F>
auto access( F&& f ) const {
auto l = lock();
return std::forward<F>(f)(this->t);
}
using base::lock;
auto lock() const { return std::shared_lock<M>(this->m); }
};