我在std::shared_mutex
中遇到了C++17
。究竟是什么std::shared_mutex
以及它与std::mutex
的区别?
答案 0 :(得分:11)
shared_mutex类是一个同步原语,可用于保护共享数据不被多个线程同时访问。与其他便于独占访问的互斥锁类型相比,shared_mutex具有两个访问级别:
- 共享 - 多个线程可以共享相同互斥锁的所有权。
- 独占 - 只有一个线程可以拥有互斥锁。
共享互斥锁通常用于多个读者可以同时访问同一资源而不会导致数据争用的情况,但只有一个编写者可以这样做。
这有多种用途,但一个常见的用途是实现一个Read Write Lock,你可以让多个线程读取共享数据,但只有一个线程可以随时独占。因此,当您有多个读者时,互斥体会以#34;共享模式"进行操作,但是当请求写入时,它会变为"独占模式"。
答案 1 :(得分:1)
std::shared_mutex
可能非常有用,尤其是在数据结构(如DNS缓存)很少更新的情况下。使用std::mutex
保护数据结构可能过于悲观,因为它消除了读取数据结构时可能的并发性
当它没有经过修改时。多个线程可以同时在同一个std::shared_mutex
上拥有共享锁。
安东尼威廉姆斯的一个例子:
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
基本上执行读操作,而update_or_add_entry
执行写操作。
所以,可以说std::shared_mutex
是一个典型的读写器互斥,因为它允许
两种不同的用法:由单个“编写者”线程独立访问或共享,
多个“读者”线程的并发访问。
答案 2 :(得分:1)
mutex
是否已锁定。
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); }
};