我有一个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,属性设置,可以处理这种情况。有吗?
答案 0 :(得分:18)
您正在寻找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
可能会被调用两次。在该示例中,密钥未传递给SomeIntensiveOperation
,但这意味着“执行有价值的东西”代码可以执行key / valueA和key / valueB对,并且只有一个结果将存储在缓存中(不一定是由SomeIntensiveOperation计算的第一个)。你需要一个并行的懒惰工厂来处理这个,如果这是一个问题。此外,由于显而易见的原因,SomeIntensiveOperation应该是线程安全的。
答案 1 :(得分:4)
检查我认为您需要的System.Collections.Concurrent命名空间ConcurrentDictionary
答案 2 :(得分:3)
使用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;
}
答案 3 :(得分:1)
我看到没有其他正确的选择,而不是使用(或多或少显式)锁(同步Hashtable只是覆盖所有带锁的方法)。
另一种选择可能是允许字典不同步。竞争条件不会破坏字典,它只需要代码进行一些多余的计算。配置代码以检查锁定或丢失的memoization是否具有更差的影响。