如何在 C++ 中单独锁定 unordered_map 元素

Posted

技术标签:

【中文标题】如何在 C++ 中单独锁定 unordered_map 元素【英文标题】:How to individually lock unordered_map elements in C++ 【发布时间】:2019-08-09 10:29:19 【问题描述】:

我有一个unordered_map,我希望它可以被多个线程访问,但是用互斥锁锁定整个东西会太慢。

为了解决这个问题,我在 unordered_map 的每个元素中放置了一个互斥锁:

class exampleClass
    std::mutex m;
    int data;
;

std::unordered_map<int,exampleClass> exampleMap;

问题是我无法安全地擦除元素,因为为了销毁互斥锁​​,它必须被解锁,但如果它被解锁,那么另一个线程可能会锁定它并在销毁期间写入或读取元素。

【问题讨论】:

向每个元素添加mutex 只会帮助您修改元素而不是删除它。擦除元素也是有问题的,因为您可能会修改整个 unordered_map,这可能会干扰访问 unordered_map 的完全不相关元素的其他线程(我不是 100% 确定,但我相信没有这样的方法就无法实现它财产)。一般来说,如果你想授予只读访问不同元素的权限,你可以简单地使用shared_mutex @ALX23z 对于 any 容器,只需检查删除元素是否会使迭代器和指针无效。如果是这样,您不仅需要在删除元素时排除对任何其他元素的任何访问,而且每次锁定互斥锁时还需要从容器中访问元素,而不是使用现有的迭代器。 【参考方案1】:

unordered_map 不适合细粒度并行。这是不合法的 在过程中添加或删除元素而不确保互斥。

我建议改用tbb::concurrent_hash_map 之类的东西,这将导致比锁定整个地图更少的锁定争用。 (还有其他并发哈希表实现;TBB 的优势在于它得到很好的支持和稳定。)

【讨论】:

【参考方案2】:

@Sneftel 的回答已经足够好了。

但是如果你坚持使用std::unordered_map,我建议你们两个使用一个互斥锁来保护映射的插入/删除,每个元素使用另一个互斥锁来修改元素。

class exampleClass
    std::mutex m;
    int data;
;

std::unordered_map<int,exampleClass> exampleMap;
std::mutex mapLock;

void add(int key, int value) 
    std::unique_lock<std::mutex> _(mapLock);
    exampleMap.insert(key, value);

void delete(int key) 
    std::unique_lock<std::mutex> _(mapLock);
    auto it = exampleMap.find(key);
    if (it != exampleMap.end()) 
        std::unique_lock<std::mutex> _1(it->m);
        exampleMap.erase(it);
     

如果delete 不是一个频繁的操作,这些应该对于整个地图上的大锁表现更好。

但是要小心这些类型的代码,因为它很难推理和正确。

我强烈推荐@Sneftel 的回答。

【讨论】:

如果您有彩色屏幕和眼睛(抱歉,如果您必须使用屏幕阅读器),您应该注意到delete 显示为蓝色。 我无法计算我尝试将函数命名为“register”的实例。【参考方案3】:

您有以下选择:

    锁定整个互斥体 使用 shared_ptr 的容器,以便可以修改与容器无关的实际类(使用或不使用互斥锁)。

【讨论】:

std::shared_ptr,就访问它指向的元素而言,与普通指针一样是线程安全的。意义 - 不是那么多。共享指针本身提供的唯一线程安全操作是控制块操作。 @Fureeish 的想法是能够锁定单个元素,同时在映射内部进行操作,如果锁定被禁止,则不会受到锁定的影响,例如,复制或移动。 那么我认为没有理由在唯一指针上使用阴影指针。而且我们缺乏确定的背景。但是,唯一的指针是默认的。

以上是关于如何在 C++ 中单独锁定 unordered_map 元素的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中获取锁?

如何读取锁定多线程 C++ 程序

如何在 C++ 中的 unordered_map 的并发读取和锁定单线程写入之间交替

在单独的 UIWindow 中锁定状态栏方向

C++ 锁定文件 Windows 阻塞模式

如何避免 C++ 匿名对象