最简单的并发C ++ 11字符串键映射

时间:2018-11-26 10:45:30

标签: c++ c++11 dictionary concurrency unordered-map

就像很多C ++开发人员一样,我需要一个简单的并发字符串键表,并且我希望它仅基于C ++ 11标准库。

“并发”是指多个线程可以在不相互锁定的情况下(大部分时间)对其进行处理。

更新:有两种流行的解决方案。并非完全“简单”,但功能丰富且性能卓越:

此外,希望节省一些时间与下一个工作人员/ gal交流,我正在分享我能够汇总的最简单 C ++ 11解决方案(〜40 LOC)。

到目前为止,反馈非常棒,它帮助我找到了现有的选择并改善了我的简单答案。很高兴看到其他简单的答案。

1 个答案:

答案 0 :(得分:0)

经过一番研究,您最终得到以下两种选择之一:

  1. 为您介绍一些超快速哈希表实现around。导入相当复杂/模糊的第三方代码,将您的密钥转换为哈希,处理碰撞,玩了一段时间。

  2. 将表分成几个较小的锁定表。使用共享的指针来管理包含的数据。

选择#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贡献者,他的话激发了我的解决方案:“如果您正在执行多线程访问,则无论如何都将必须分开”。 (很抱歉,我的脑袋后面写着这个字,找不到原始报价!)