Tengo un problema interesante con interbloqueos en mi aplicación. Hay un almacén de datos en memoria que usa un ReaderWriterLockSlim para sincronizar lecturas y escrituras. Uno de los métodos de lectura usa Parallel.ForEach para buscar en la tienda dado un conjunto de filtros. Es posible que uno de los filtros requiera una lectura constante de la misma tienda. Aquí está el escenario que está produciendo un punto muerto:Interbloqueo en Paralelo.ParaEach con ReaderWriterLockSlim
ACTUALIZACIÓN: Ejemplo de código a continuación. Pasos actualizados con el método real de las llamadas
Dada instancia singleton store
de ConcreteStoreThatExtendsGenericStore
- Thread1 consigue un bloqueo de lectura en la tienda -
store.Search(someCriteria)
- Thread2 intentos para actualizar la tienda con un bloqueo de escritura -
store.Update()
- , bloques detrás de Thread1 - Thread1 ejecuta Parallel.F orEach en el almacén para ejecutar un conjunto de filtros
- Thread3 (generados por Thread1 's Parallel.ForEach) intenta una lectura del almacén constante en el tiempo. Intenta obtener un bloqueo de lectura pero está bloqueado detrás del bloqueo de escritura de Thread2.
- Thread1 no puede terminar porque no puede unirse a Thread3. Thread2 no puede terminar porque está bloqueado detrás de Thread1.
Lo ideal sería que lo que me gustaría hacer es no tratar de adquirir un bloqueo de lectura si un hilo ancestro del hilo actual ya tiene la misma cerradura. ¿Hay alguna manera de hacer esto? ¿O hay otro enfoque/mejor?
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;
}
}
¿La tienda en la memoria es un tipo personalizado o es una lista que es un campo dentro de otra clase? –
Pase lo que haya bloqueado en el método que está ejecutando en foreach; de lo contrario, la paralelización de la lectura en adelante es una pérdida de tiempo. Nunca se ejecutará en paralelo porque todos están embotellados en el mismo recurso. –
@TrevorPilley: La tienda es un diccionario, con Parallel.ForEach sobre los valores –
eakins05