【问题标题】:Using a hashtable inside a Parallel.ForEach?在 Parallel.ForEach 中使用哈希表?
【发布时间】:2009-11-01 18:20:06
【问题描述】:

我有一个 Parallel.ForEach 循环在体内运行密集操作。

该操作可以使用 Hashtable 来存储值,并且可以重复用于其他连续的循环项。我在密集操作完成后添加到Hashtable中,下一个循环项可以在Hashtable中查找并​​重用对象,而不是再次运行密集操作。

但是,因为我使用的是 Parallel.ForEach,所以存在一个不安全的问题,导致 Hashtable.Add 和 ContainsKey(key) 调用不同步,因为它们可能并行运行。引入锁可能会导致性能问题。

示例代码如下:

Hashtable myTable = new Hashtable;
Parallel.ForEach(items, (item, loopState) =>
{
    // If exists in myTable use it, else add to hashtable
    if(myTable.ContainsKey(item.Key))
    {
       myObj = myTable[item.Key];
    }
    else
    {
       myObj = SomeIntensiveOperation();
       myTable.Add(item.Key, myObj); // Issue is here : breaks with exc during runtime
    }
    // Do something with myObj
    // some code here
}

TPL 库中必须有一些 API,属性设置,可以处理这种情况。有吗?

【问题讨论】:

    标签: c# .net parallel-extensions task-parallel-library


    【解决方案1】:

    您正在寻找System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>。新的并发集合使用显着改进的锁定机制,并且应该在并行算法中表现出色。

    编辑:结果可能如下所示:

    ConcurrentDictionary<T,K> cache = ...;
    Parallel.ForEach(items, (item, loopState) =>
    {
        K value;
        if (!cache.TryGetValue(item.Key, out value))
        {
            value = SomeIntensiveOperation();
            cache.TryAdd(item.Key, value);
        }
    
        // Do something with value
    } );
    

    警告词:如果items 中的元素并非都具有唯一的item.Key,那么SomeIntensiveOperation 可能会为该键调用两次。在示例中,key 没有传递给SomeIntensiveOperation,但这意味着“Do something with value”代码可以执行 key/valueA 和 key/valueB 对,并且只有一个结果会存储在缓存中(不是也必然是 SomeIntensiveOperation 计算的第一个)。你需要一个并行的惰性工厂来处理这个如果这是一个问题。此外,出于显而易见的原因,SomeIntensiveOperation 应该是线程安全的。

    【讨论】:

    • @AdamRalph :因为他正在使用 TPL 库,所以他已经在使用 .net 4.0
    • @Adam & Yassir:正确,新系列的设计考虑了 Parallel LINQ。
    • 是的,感谢您的回答和 cmets
    【解决方案2】:

    检查System.Collections.Concurrent命名空间我认为你需要ConcurrentDictionary

    【讨论】:

      【解决方案3】:

      使用 ReaderWriterLock,这对于具有大量读取和少量写入且持续时间短的工作具有良好的性能。您的问题似乎符合此规范。

      所有读取操作都将快速运行并且无锁,唯一会阻塞任何人的时间是发生写入时,并且该写入只是将某些内容推入 Hashtable 所需的时间。

      ReaderWriterLockSlim on MSDN

      我想我会扔掉一些代码......

      ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
      Hashtable myTable = new Hashtable();
      Parallel.ForEach(items, (item, loopState) =>
      {
          cacheLock.EnterReadLock();
          MyObject myObj = myTable.TryGet(item.Key);
          cacheLock.ExitReadLock();
      
          // If the object isn't cached, calculate it and cache it
          if(myObj == null)
          {
             myObj = SomeIntensiveOperation();
             cacheLock.EnterWriteLock();
             try
             {
                 myTable.Add(item.Key, myObj);
             }
             finally
             {
                 cacheLock.ExitWriteLock();
             }           
          }
          // Do something with myObj
          // some code here
      }
      
      static object TryGet(this Hashtable table, object key)
      {
          if(table.Contains(key))
              return table[key]
          else
              return null;
      }
      

      【讨论】:

      • ".NET Framework 有两个读写器锁,ReaderWriterLockSlim 和 ReaderWriterLock。建议在所有新开发中使用 ReaderWriterLockSlim。ReaderWriterLockSlim 类似于 ReaderWriterLock,但它简化了递归规则以及升级和降级规则锁状态。ReaderWriterLockSlim 避免了很多潜在死锁的情况。另外,ReaderWriterLockSlim 的性能明显优于 ReaderWriterLock。"
      • 这个建议似乎很合理,所以我更新了我的答案。感兴趣的可以看看这篇 MSDN 杂志文章:msdn2.microsoft.com/en-us/magazine/cc163599.aspx
      • 为什么这与HashTable.Synchronized() 导致的双线程竞争条件没有相同的问题,其中两个线程都从TryGet 返回null,然后都计算myObj 并尝试添加它?
      • 这只是为了对哈希表进行原子访问;它不进行密钥冲突检测,但最坏的情况应该是效率较低的最后写入获胜(> 1 个线程做同样的工作)。 Syncrhonized() 使用的锁也有可能是静态的,原因是向后兼容,而 ReaderWriterLockSlim 显然已经更新,通过不同的锁定模型避免了许多旧的死锁情况。
      【解决方案4】:

      我认为除了使用(或多或少显式)锁之外没有其他正确的选择(同步的 Hashtable 只是用锁覆盖所有方法)。

      另一个选项可能是允许字典不同步。竞争条件不会破坏字典,它只需要代码进行一些多余的计算。分析代码以检查锁定或丢失记忆是否具有更差的影响。

      【讨论】:

        猜你喜欢
        • 2020-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-31
        • 1970-01-01
        • 2023-03-27
        • 2014-06-29
        • 2020-04-12
        相关资源
        最近更新 更多