不断迭代的线程安全的 Unordered_map

Posted

技术标签:

【中文标题】不断迭代的线程安全的 Unordered_map【英文标题】:Thread-Safe Unordered Map that is being Continuously Iterated Over 【发布时间】:2015-05-26 21:43:49 【问题描述】:

我有可以使用以下模板访问的类集合:

template <typename T> using RegistryMap = std::unordered_map <std::string, T *>;
template <typename T> class Registry 
  static RegistryMap<T> registry;

public:
  static T* get(const std::string& name) 
    auto it = registry.find(name);
    return it == registry.end() ? nullptr : it->second;
  

  static const RegistryMap<T>& getAll() 
    return registry;
  

 static bool add(const std::string &name, T *object) 
    T* &store = registry[name];
    if (store)
      return false;
    else 
      store = object;
      return true;
    
  

  static bool remove(const std::string &name) 
    auto it = registry.find(name);
    if (it == registry.end())
      return false
    else 
      registry.erase(it);
      return true;
    
  
;

此注册表中的许多类都定义了一个名为 run 的方法,线程线程将调用该方法的紧密循环。发生这种情况时,其他线程可能会使用上述方法从地图中添加/删除元素。

void workerThread() 
     examples = Registry<ExampleClass>::getAll();
     auto it = examples.begin();
     while (true) 
         if (it != examples.end())
             it++->second->run();
         else
             it = examples.begin();
     

有没有办法让这个模板线程安全?我可以向 Registry 模板类添加一个静态锁,并在 add 或 remove 方法中获取它。但是我该如何处理紧环线呢?特别是在注册表变得非常大的情况下。如有必要,可以替换 unordered_map 并损失线性查找时间。

【问题讨论】:

为什么不简单地使用循环链表呢?这样你根本不需要锁定,只需要确保你以正确的顺序进行插入/删除...... @Amit 链表在某些情况下可能是完美的,但它不允许 O(1) (或接近它)访问某些命名条目。据推测,OP 想要这样的东西。 @AmiTavory - 引用:如有必要,可以替换 unordered_map 并失去线性查找时间。不仅如此,如果 O(1) 很重要,则可以使用映射将键链接到列表中的链接以便直接访问。 @Amit 你是对的 - 错过了。 OP 有可能(或不可能)更愿意 保留 O(1) 访问权限,尽管他愿意 失去它。尽管如此,您上面的两个 cmets 是正确的。 @AmiTavory - 你说服我添加这个作为答案:-) 【参考方案1】:

一种简单的方法是使用循环链表。这根本不需要锁定 - 只需确保插入和删除是列表本身的原子操作(很容易实现)。

如果线性查找很重要,可以通过在键(字符串...)和列表中的链接之间保存映射来扩展解决方案,以便于删除或任何其他操作。

【讨论】:

【参考方案2】:

有无数种方法。这是一个非常简单的方法,实现错误的可能性很小。

在您的内部实现中,不要保留单个哈希表,而是 p 对哈希表和锁(p 是一些素数)。

要修改(即插入或删除)条目,首先找到其哈希值,然后将其取模到 p 表之一,然后锁定并执行操作。

与此同时,紧密循环线程将遍历 p 组。对于每个组,它将锁定锁,遍历该组,然后释放它。

p 中有一个权衡。 p 的值太小(相对于对象的总数)将导致锁定竞争的高概率。 p 的值太高将导致紧密循环线程的锁定到有效负载操作开销很高。

根据情况,您可能还需要考虑读写锁,只有修改操作才具有写访问权限。

【讨论】:

以上是关于不断迭代的线程安全的 Unordered_map的主要内容,如果未能解决你的问题,请参考以下文章

迭代 ConcurrentHashMap 值线程安全吗?

线程安全的 C++ std::set 支持从多个线程添加、删除和迭代器

std::map 访问线程是不是安全,如果它的迭代器永远不会失效

STL的线程安全解决方案

如何安全地迭代互锁的 slist?

源码阅读(33):Java中线程安全的QueueDeque结构——ArrayBlockingQueue