Deadlock in Parallel.ForEach with ReaderWriterLockSlim

I have an interesting problem with deadlocks in my application. There is an in-memory data store that uses ReaderWriterLockSlim to synchronize read and write. One of the reading methods uses Parallel.ForEach to search the store based on a set of filters. Perhaps one of the filters requires constant reading of the same storage. Here is the deadlock scenario:

UPDATE: Sample code below. Steps Updated with Real Method Calls
Given a Single Instance storeofConcreteStoreThatExtendsGenericStore

  • Thread1 gets a read lock in the repository -store.Search(someCriteria)
  • Thread2 is trying to update the storage with a write lock store.Update()-, locks Thread1
  • Thread1 runs Parallel.ForEach against the repository to run a set of filters.
  • Thread3 (spawned by Thread1 Parallel.ForEach) is trying to read constant time into storage. It tries to get a read lock, but is locked behind a Thread2 write lock .
  • Thread1 cannot complete because it cannot join Thread3 . Thread2 cannot complete because it is blocked by Thread1 .

Ideally, what I would like to do is not try to get a read lock if the predecessor thread of the current thread already has the same lock. Is there any way to do this? Or is there a different 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;
    }
}
+5
1

, concurrency, , , .

.Net 4.0, Dictionary ConcurrentDictionary ReaderWriterLockSlim. , , , ( ), , , , . , .

, (new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion)), . ?

( ), . , , , , .

+1

All Articles