アプリケーションのデッドロックに関する興味深い問題があります。ReaderWriterLockSlim を使用して読み取りと書き込みを同期するインメモリ データ ストアがあります。読み取りメソッドの 1 つは、Parallel.ForEach を使用して、一連のフィルターを指定してストアを検索します。フィルターの 1 つが同じストアの一定時間の読み取りを必要とする可能性があります。デッドロックが発生するシナリオは次のとおりです。
更新:以下のコード例。
実際のメソッド呼び出しで更新されたstore
ステップConcreteStoreThatExtendsGenericStore
- スレッド1 はストアで読み取りロックを取得します -
store.Search(someCriteria)
- スレッド 2は、書き込みロックでストアを更新しようとします - -、スレッド 1
store.Update()
の後ろのブロック - Thread1はストアに対して Parallel.ForEach を実行し、一連のフィルターを実行します。
- Thread3 ( Thread1の Parallel.ForEach によって生成される) は、ストアの一定時間の読み取りを試みます。読み取りロックを取得しようとしますが、Thread2の書き込みロックの背後でブロックされます。
- Thread1はThread3に参加できないため、終了できません。 Thread2はThread1の背後でブロックされているため、終了できません。
理想的には、現在のスレッドの祖先スレッドが既に同じロックを持っている場合、読み取りロックを取得しようとしないことです。これを行う方法はありますか?または、別の/より良いアプローチがありますか?
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;
}
}