[英]Deadlock in Parallel.ForEach with ReaderWriterLockSlim
I have an interesting problem with deadlocks in an my application. 我的应用程序中有一个有趣的死锁问题。 There is an in-memory data store that uses a ReaderWriterLockSlim to synchronize reads and writes.
有一个内存数据存储,它使用ReaderWriterLockSlim来同步读写。 One of the read methods uses Parallel.ForEach to search the store given a set of filters.
其中一种读取方法使用Parallel.ForEach在给定一组过滤器的情况下搜索商店。 It's possible that one of the filters requires a constant-time read of same store.
其中一个过滤器可能需要对同一商店进行恒定时间读取。 Here is the scenario that's producing aa deadlock:
这是产生死锁的场景:
UPDATE: Example code below. 更新:下面的示例代码。 Steps updated with actual method calls
使用实际方法调用更新步骤
Given singleton instance store
of ConcreteStoreThatExtendsGenericStore
给定
ConcreteStoreThatExtendsGenericStore
单例实例store
store.Search(someCriteria)
store.Search(someCriteria)
store.Update()
-, blocks behind Thread1 store.Update()
- ,后面线程1块 Ideally what I'd like to do is not try to acquire a read lock if an ancestor thread of the current thread already has the same lock. 理想情况下,如果当前线程的祖先线程已经具有相同的锁,我想要做的就是不尝试获取读锁。 Is there any way to do this?
有没有办法做到这一点? Or is there a another/better approach?
或者还有另一种/更好的方法吗?
public abstract class GenericStore<TKey, TValue>
{
private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private List<IFilter> _filters; //contains instance of ExampleOffendingFilter
protected Dictionary<TKey, TValue> Store { get; private set; }
public void Update()
{
_lock.EnterWriterLock();
//update the store
_lock.ExitWriteLock();
}
public TValue GetByKey(TKey key)
{
TValue value;
//TODO don't enter read lock if current thread
//was started by a thread holding this lock
_lock.EnterReadLock();
value = Store[key];
_lock.ExitReadLock();
return value;
}
public List<TValue> Search(Criteria criteria)
{
List<TValue> matches = new List<TValue>();
//TODO don't enter read lock if current thread
//was started by a thread holding this lock
_lock.EnterReadLock();
Parallel.ForEach(Store.Values, item =>
{
bool isMatch = true;
foreach(IFilter filter in _filters)
{
if (!filter.Check(criteria, item))
{
isMatch = false;
break;
}
}
if (isMatch)
{
lock(matches)
{
matches.Add(item);
}
}
});
_lock.ExitReadLock();
return matches;
}
}
public class ExampleOffendingFilter : IFilter
{
private ConcreteStoreThatExtendsGenericStore _sameStore;
public bool Check(Criteria criteria, ConcreteValueType item)
{
_sameStore.GetByKey(item.SomeRelatedProperty);
return trueOrFalse;
}
}
It's unclear what kind of concurrency, memory and performance requirements you actually have so here are a few options. 目前还不清楚你实际拥有什么样的并发性,内存和性能要求,所以这里有几个选项。
If you are using .Net 4.0, you could replace your Dictionary
with a ConcurrentDictionary
and remove your ReaderWriterLockSlim
. 如果您使用的是.Net 4.0,则可以使用
ConcurrentDictionary
替换Dictionary
并删除ReaderWriterLockSlim
。 Keep in mind that doing that will reduce your locking scope and change your method semantics, allowing changes to the contents while you're enumerating (among other things), but on the other hand that will give you a threadsafe enumerator that won't block reads or writes. 请记住,这样做会减少锁定范围并更改方法语义,允许在枚举(包括其他内容)时更改内容,但另一方面,这将为您提供不会阻塞的线程安全枚举器读或写。 You'll have to determine if that's an acceptable change for your situation.
您必须确定这是否适合您的情况。
If you really do need to lock down the entire collection in this way, you might be able to support a recursive lock policy ( new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion)
) if you can keep all operations on the same thread. 如果您确实需要以这种方式锁定整个集合,那么您可以支持递归锁定策略(
new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion)
),如果您可以在同一个线程上保留所有操作。 Is performing your search in parallel a necessity? 并行执行搜索是必要的吗?
Alternately, you may want to just get a snapshot of your current collection of values (locking around that operation) and then perform your search against the snapshot. 或者,您可能只想获取当前值集的快照(锁定该操作),然后针对快照执行搜索。 It won't be guaranteed to have the latest data and you'll have to spend a little time on conversion, but maybe that's an acceptable tradeoff for your situation.
我们无法保证获得最新数据,您将不得不花一点时间进行转换,但也许这对您的情况来说是可以接受的权衡。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.