the*_*man 6 c# multithreading dictionary locking task-parallel-library
我有一些代码可以response并行处理我的数据库中的许多对象(使用AsParallel()).每个response都有很多components.该responses可以共享相同的组件.我对component数据进行了一些修改并将其保存到db中,因此我需要防止多个线程同时处理同一个component对象.
我使用锁来实现这一目标.我有一个ConcurrentDictionary<int, object>可以容纳所有必要的锁定对象.像这样:
private static ConcurrentDictionary<int, object> compLocks = new ConcurrentDictionary<int, object>();
var compIds = db.components.Select(c => c.component_id).ToList();
foreach (var compId in compIds)
{
compLocks[compId] = new object();
}
Run Code Online (Sandbox Code Playgroud)
然后我会这样做:
responses.AsParallel().ForAll(r =>
{
... do some time consuming stuff with web services ...
// this is a *just in case* addition,
// in case a new component was added to
// the db since the dictionary was constructed
// NOTE: it did not have any effect, and I'm no longer
// using it as @Henk pointed out it is not thread-safe.
//if (compLocks[c.component_id] == null)
//{
// compLocks[c.component_id] = new object();
//}
componentList.AsParallel().ForAll(c =>
{
lock (compLocks[c.component_id])
{
... do some processing, save the db records ...
}
});
});
Run Code Online (Sandbox Code Playgroud)
这似乎运行得很好但是在程序执行结束时(由于有大量数据,它运行了几个小时)我得到以下异常:
未处理的异常:System.AggregateException:发生一个或多个错误.---> System.Collections.Generic.KeyNotFoundException:字典中没有给定的键.at System.Collections.Concurrent.ConcurrentDictionary`2.get_Item(TKey key)
我确信ConcurrentDictionary正在填充每个可能的componentID.
我有3个问题:
ConcurrentDictionary这个吗?为了弄清楚所有这一切的原因是什么,它.AsParallel()不会枚举集合responses.它是惰性评估的,意味着可以在运行时(来自其他进程)将新的responses(以及新的components)添加到集合中..ToList()在.AsParallel()修复问题之前执行快照.
我在运行时添加componentID的代码compLocks没有解决这个问题是因为它不是线程安全的.
1)这个例外怎么可能?
显然是这样,但不仅仅是从发布的代码来看。如果将数据添加到数据库中,就会发生这种情况(是否可以选择预先捕获responses?ToList())
2)我需要一个 ConcurrentDictionary 吗?
不是使用固定列表,但是当解决方案涉及缺失时添加时,那么是的,您需要一个并发集合。
3)我对这种情况下锁定如何工作的理解是否正确/是否有更好的方法?
不完全确定。锁定看起来不错,但您仍然需要多次处理重复项。只是不同时而已。
对编辑的反应:
if (compLocks[c.component_id] == null)
{
compLocks[c.component_id] = new object();
}
Run Code Online (Sandbox Code Playgroud)
这不是线程安全的。现在可以为 1 个 component_id 值创建多个锁定对象。您需要使用其中一种GetOrAdd()方法。
但我不希望这会给出您所得到的异常,所以这可能不是直接的问题。