Ho un problema interessante con deadlock in una mia applicazione. Esiste un archivio dati in memoria che utilizza ReaderWriterLockSlim per sincronizzare letture e scritture. Uno dei metodi di lettura utilizza Parallel.ForEach per cercare nel negozio una serie di filtri. È possibile che uno dei filtri richieda una lettura costante dello stesso negozio. Ecco lo scenario che sta producendo un deadlock:Deadlock in Parallel.ForEach con ReaderWriterLockSlim
UPDATE: Esempio di codice qui sotto. Passi aggiornati con metodo effettivo chiamate
determinata istanza Singleton store
di ConcreteStoreThatExtendsGenericStore
- Thread1 ottiene un blocco di lettura per l'archivio -
store.Search(someCriteria)
- Thread2 tentativi di aggiornare l'archivio con un blocco di scrittura -
store.Update()
- , blocchi dietro Thread1 - Thread1 esegue Parallel.F foreach contro il negozio per eseguire una serie di filtri
- Thread3 (generati da Filettatura1 s' Parallel.ForEach) tenta un tempo costante lettura del negozio. Prova a ottenere un blocco di lettura ma è bloccato dietro il blocco scrittura Thread2.
- Thread1 Impossibile completare perché non è possibile unire Thread3. Thread2 non può terminare perché è bloccato dietro Thread1.
Idealmente quello che mi piacerebbe fare è non cercare di acquisire un blocco di lettura, se un thread antenato del thread corrente ha già lo stesso blocco. C'è un modo per fare questo? O c'è un altro/approccio migliore?
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;
}
}
L'archivio in memoria è un tipo personalizzato o è un elenco che è un campo all'interno di un'altra classe? –
Passa ciò che hai bloccato nel metodo che stai utilizzando in foreach, altrimenti parallelarlo da leggere in avanti è una perdita di tempo. Non funzioneranno mai in parallelo perché sono tutti con bottleneck sulla stessa risorsa. –
@TrevorPilley: Il negozio è un dizionario, con Parallel.ForOgni sopra i valori –
eakins05