使用Parallel.ForEach内部的哈希表?
-
11-09-2019 - |
题
我有运行体内部的密集操作的Parallel.ForEach循环。
的操作可以使用哈希表来存储的值,并且可以为其它连续循环项被重复使用。我添加到Hashtable中密集的操作完成后,下一个循环的项目可以查找在Hashtable和重用的对象,而不是再次运行密集型操作。
不过,因为我使用Parallel.ForEach存在不安全的问题,引起了Hashtable.Add和使用containsKey(密钥)的调用不同步,因为它们可能会并行运行。引入锁可能会导致PERF的问题。
这里的示例代码:
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
}
必须有一些API,属性设置TPL库中,可能会处理这种情况。是否有?
解决方案
您正在寻找 System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>
。新的并发集合使用显著改进的锁定机构,应在并行算法excellectly执行。
编辑:结果可能是这样的:
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
可以得到该键的叫了两声。在这个例子中,关键是不能传递给SomeIntensiveOperation
,但它意味着,“不要用一些有价值的东西”的代码可以执行键/值a和键/ VALUEB对,结果只有一个会被存储在缓存中(不一定是第一个由SomeIntensiveOperation计算任一)。你需要一个平行懒工厂来处理这个的如果的这是一个问题。此外,出于显而易见的原因SomeIntensiveOperation应该是线程安全的。
其他提示
查看 System.Collections中。并发命名空间,我认为你需要 ConcurrentDictionary 一>
使用一个ReaderWriterLock,这对工作出色的表现,有许多读取和一些是短时间的写入。您的问题似乎符合该规范。
所有读操作会很快运行,并锁定自由,只有一次有人将被阻塞是当写入正在发生的事情,那写只是只要它采取推在一个Hashtable的东西。
我想我会扔了一些代码...
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;
}
我认为没有其他的正确选择使用比(更多或更少显式)的锁(A同步的Hashtable只是覆盖与锁的所有方法)。
另一个选项可以是,以允许字典去同步。比赛情况不会损坏的字典,它只是需要的代码做一些多余的计算。配置文件的代码来检查锁定或丢失的memoization是否具有效果更差。