Multithreading unordered_map
Multithreading unordered_map
我在多线程环境中工作。基本上,我有一个可以同时由多个线程访问的unordered_map
。现在,我的算法是:
function foo(key) {
scoped_lock()
if key exists {
return Map[key]
}
value = get_value()
Map[key] = value
}
显然,这种实现的性能并不好。我可以使用任何算法/方法来提高性能吗?
编辑:
我做了一些测试,我想到了双重检查锁定。因此,我修改了代码:
function foo(key) {
if key exists {
return Map[key]
}
scoped_lock()
if key exists {
return Map[key]
}
value = get_value()
Map[key] = value
}
实际上我只在 scoped_lock() 之前添加另一个检查。在这种情况下,假设函数被调用N
次。如果前m
次调用foo
,其中m < N
填充了地图,而接下来的N - m
次调用仅从地图中获取值,则我不需要独占访问权限。此外,scoped_lock
后还有另一项检查,可确保线程安全。我说的对吗?无论如何,对于第一个代码,执行需要~208s,而第二个代码需要~200s。
下面是一个实用程序类:
template<class T, class M=std::mutex, template<class...>class S=std::unique_lock, template<class...>class U=std::unique_lock>
struct mutex_protected {
template<class F>
auto read( F&& f ) const
-> typename std::result_of<F&&(T const&)>::type
{
auto l = lock();
return std::forward<F>(f)(data);
}
template<class F>
auto write( F&& f )
-> typename std::result_of<F&&(T&)>::type
{
auto l = lock();
return std::forward<F>(f)(data);
}
mutex_protected(mutex_protected&&)=delete;
mutex_protected& operator=(mutex_protected&&)=delete;
template<class...Args>
mutex_protected( Args&&...args ):
data( std::forward<Args>(args)... )
{}
private:
mutable M m;
T data;
U<M> lock() { return U<M>(m); }
S<M> lock() const { return S<M>(m); }
};
它,特别是在 C++14 中,允许您以易于编写的方式与受互斥锁保护的数据实例进行交互。
在 c++14 中,您可以使用std::shared_timed_mutex
,在 c++17 中,您可以使用如下所示std::shared_mutex
:
template<class T>
using rw_guarded = mutex_guarded< T, std::shared_mutex, std::shared_lock >;
这样就可以同时拥有多个阅读器。 但是您应该首先确定简单的互斥锁是否足够快。
struct cache {
using Key=std::string;
using Value=int;
using Map=std::unordered_map< Key, Value >;
Value get( Key const& k ) {
Value* r = table.read([&](Map const& m)->Value*{
auto it = m.find(k);
if (it == m.end()) return nullptr;
return std::addressof( it->second );
});
if (r) return *r;
return table.write([&](Map& m)->Value{
auto it = m.find(k);
if (it != m.end()) return it->second;
auto r = m.insert( std::make_pair(k, 42) ); // construct data here
return r.first->second;
});
}
private:
mutex_guarded< std::unordered_map< Key, Value > > table;
};
将mutex_guarded
升级到rw_guarded
,它将切换到读写器锁。
这是一个更复杂的版本:
有两张地图;一张是价值地图,一张是共享价值的未来。
使用读取器写入器锁(也称为共享互斥锁)。
要获取,请获取共享锁。 检查它是否存在。 如果是,请返回。
解锁第一张地图。 锁定第二个地图进行写入。 如果密钥下还没有共同的未来,请添加一个。 解锁地图 2,并等待共享未来,无论您是否添加了它。
完成后,锁定第一个地图进行读取;检查结果是否已经存在。 如果是,请将其退回。 如果没有,请解锁,重新锁定写入,将数据移动到映射 1 中(如果尚未存在),在第一个映射中返回数据。
这旨在最大限度地减少周期映射 1 被单独锁定,从而允许在那里实现最大并发性。
其他设计将优化其他考虑因素。
不要使用operator[]
。不要在没有激活某种锁的情况下与任何地图进行交互。 知道哪些锁对应于哪个地图。 请注意,在某些情况下,可以在没有锁定的情况下读取元素(不查找)。 有时需要阅读共享内容的副本,而不是共享内容。 查找每种类型的文档以确定哪些操作需要哪些锁。
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- 使用一个考虑到std::map中键值的滚动或换行的键
- 为什么 const std::p air<K,V>& 在 std::map 上基于范围的 for 循环不起作用?
- 允许从 std::map 的密钥窃取资源?
- 有没有办法对std::unordered_set、std::unrdered_map、std::set、std::map
- 将重物插入std::map
- 使用通用值初始化 std::map,不重复
- 仅包含可移动 std::map 的类的移动构造函数不起作用
- C++:当所有条目都保证是唯一时,替代 std::map
- 使用模板化的键类型定义 std::map,该键类型基于作为参数接收的函数
- 如果 KEY 是 std::list 或 std::vector 而不是值,那么 std::map 的默认行为是什么?
- 使用字符数组作为 Map 中的键
- C++如何创建 std::map
- C++ equivalent to Java Map getOrDefault?
- 从其他容器中移动构造"std::map"
- 如何使用 uint64_t 键类型从 std::map<int, std::string> 返回值?
- 将 std::map::emplace 与返回 shared_ptr 的函数一起使用是否正确?
- C++中 std::map 的运行时复杂度是多少?
- unordered map -在c++ std::unordered_map中预分配桶