就像很多C ++开发人员一样,我需要一个简单的并发字符串键表,并且我希望它仅基于C ++ 11标准库。
“并发”是指多个线程可以在不相互锁定的情况下(大部分时间)对其进行处理。
更新:有两种流行的解决方案。并非完全“简单”,但功能丰富且性能卓越:
此外,希望节省一些时间与下一个工作人员/ gal交流,我正在分享我能够汇总的最简单 C ++ 11解决方案(〜40 LOC)。
到目前为止,反馈非常棒,它帮助我找到了现有的选择并改善了我的简单答案。很高兴看到其他简单的答案。
答案 0 :(得分:0)
经过一番研究,您最终得到以下两种选择之一:
为您介绍一些超快速哈希表实现around。导入相当复杂/模糊的第三方代码,将您的密钥转换为哈希,处理碰撞,玩了一段时间。
将表分成几个较小的锁定表。使用共享的指针来管理包含的数据。
选择#2几乎没有争执地解决了我当前的用例,同时没有关闭将来的优化之门,您将永远需要它。它实际上解决了您需要“成长”为更复杂的解决方案的一些问题。方法如下:
template <typename Element, unsigned NumBlocks>
class ConcurrentTable
{
using SharedElement = std::shared_ptr<Element>;
class InnerMap {
std::mutex mut_;
std::unordered_map<std::string,SharedElement> map_;
public:
SharedElement get(std::string const& key) {
std::unique_lock<mutex> lock(mut_);
auto i = map_.find(key);
return (i != map_.end()) ? i->second
: SharedElement{}; // Empty pointer == not found
}
bool set(std::string const& key, SharedElement && value) {
std::unique_lock<mutex> lock(mut_);
auto [_,created] = map_.insert_or_assign(key, forward<SharedElement>(value));
return created;
}
bool del(std::string const& key) {
unique_lock<mutex> lock(mut_);
return map_.erase(key);
}
};
std::array<InnerMap, NumBlocks> maps_;
std::hash<std::string> hash_;
InnerMap& map_for(std::string const& key) {
return maps_[hash_(key) % NumBlocks];
}
public:
SharedElement get(std::string const& key) {
return map_for(key).get(key);
}
bool set(std::string const& key, SharedElement && value) {
return map_for(key).set(key, std::forward<SharedElement>(value));
}
bool del(std::string const& key) {
return map_for(key).del(key);
}
};
现在您可以将并发字符串表实例化为这样的字符串:
ConcurrentTable<std::string,128> my_concurrent_table;
有128个子表,有足够的散列空间,例如10个并发线程随机访问表。如果您需要更多线程或更少争用,请增加大小。
信用归功于一位SO贡献者,他的话激发了我的解决方案:“如果您正在执行多线程访问,则无论如何都将必须分开”。 (很抱歉,我的脑袋后面写着这个字,找不到原始报价!)