2011-12-25 14 views
5

ho questo metodo:istruzione lock non sembra funzionare

public bool Remove(EntityKeyType key) 
{ 
    lock (syncroot) 
    { 
     //wait if we need to 
     waitForContextMRE.Wait(); 

     //if the item is not local, assume it is not remote. 
     if (!localCache.ContainsKey(key)) return false; 

     //build an expression tree 
     Expression<Func<EntityType, bool>> keyComparitorExpression = GenerateKeyComparitorExpression(key); 

     var itemToDelete = TableProperty.Single(keyComparitorExpression); 

     //delete from db 
     TableProperty.DeleteOnSubmit(itemToDelete); 
     DataContext.SubmitChanges(); 

     //get the removed item for OnCollectionChanged 
     EntityType itemToRemove = localCache[key]; 
     itemToRemove.PropertyChanged -= item_PropertyChanged; 

     //remove from the list 
     Debug.Assert(localCache.Remove(key)); 

     //call the notification 
     OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, itemToRemove)); 
     return true; 
    } 
} 

sto chiamando da più thread (chiamando lo stesso esempio), ma un'eccezione continua ad essere gettato su TableProperty.Single (Sequenza contiene nessun elemento). Dopo aver eseguito il debug del codice, ho visto che si sta creando una situazione in cui l'elemento viene eliminato dal database dopo che un thread diverso ha verificato la presenza della cache. Questo non dovrebbe essere possibile a meno che non ci siano più thread all'interno dell'istruzione lock (l'oggetto syncroot è sicuramente la stessa istanza tra thread).

Impossibile? Ho la prova: Impossible situation

Ci sono tre thread all'interno dell'istruzione lock! Cosa dà?

note:

  1. Il MRE è impostata (non il blocco).
  2. Questa non è una situazione in cui viene generata l'eccezione, mostra solo più thread all'interno di una sezione di blocco. Aggiornamento: Ho modificato l'immagine in un evento Intellitrace dell'eccezione. L'immagine precedente è here
  3. L'oggetto syncroot non è statico, perché desidero solo le chiamate alla stessa istanza sincronizzate.

Aggiornamento

Questa è la dichiarazione dell'oggetto SyncRoot:

private object syncroot = new object(); 

Ed alcune altre dichiarazioni:

private ManualResetEventSlim waitForContextMRE = new ManualResetEventSlim(true); 
private DataContextType _dataContext; 
private System.Data.Linq.Table<EntityType> _tableProperty; 
//DataContextType and EntityType are generic type parameters 

non posso fare lo SyncRoot statica perché ho diverse istanze della classe in esecuzione ed è importante che non si blocchino k a vicenda. Ma questo non ha molta importanza: renderlo statico non risolve il problema.

ManualResetEvent (waitForContextMRE) non è presente per la sincronizzazione: è lì per bloccare le operazioni del database per un certo periodo di tempo dopo l'esecuzione di determinate operazioni (ad esempio all'avvio). È impostato per la maggior parte del tempo. Toglierlo dal blocco di blocco non risolve il problema.

+1

A: possiamo vedere dove stai inizializzando 'syncroot', e B: per quanto tempo è in gioco il contesto oggetto? C: sei sicuro che siano la stessa istanza? Difficile da dire dallo stato di pausa ... –

+1

Suggerirei di aggiungere messaggi di traccia, con System.Diagnostics.Debug.WriteLine. Scrivi l'id del thread e l'hashcode syncroot all'inizio e alla fine del blocco di blocco. Quindi puoi facilmente vedere nella finestra di output se due thread si sovrappongono allo stesso syncroot, cosa che sicuramente non dovrebbe accadere. –

+0

Qual è il danno nel rendere Syncroot statico? –

risposta

0

Ho eseguito il debug di questo problema per un po 'di tempo e, anche se non l'ho risolto, mi è chiaro che i blocchi funzionano.Sto indovinando il problema è con il DataContext (che è noto per essere ingannevole in situazioni multithread).

+0

Ho trovato il problema - 'Debug.Assert (localCache.Remove (chiave))' - Proprio come in C++, la roba all'interno della dichiarazione non viene eseguita in modalità di rilascio, causando l'eliminazione degli elementi dal database e non dalla cache . –

3

L'unica spiegazione che ho è waitForContextMRE.Wait(); chiamare questo fa sì che il thread sblocchi syncroot! E così l'altro thread può entrare nella sezione di blocco. Prova a spostare waitForContextMRE.Wait(); prima della serratura (...).

+0

Non penso che possiamo vedere abbastanza su waitForContextMRE per giungere a delle conclusioni; 'Monitor.Wait (syncroot)' è l'unica cosa che rilascerebbe il blocco - e non è ovvio che sia lo stesso –

+0

Perché chiamare wait() se ci si trova all'interno del blocco e si presuppone che non ci sia altro processo usando il filo? // aspetta se abbiamo bisogno di waitForContextMRE.Wait(); –

+0

Provato - non risolve il problema, purtroppo ... –

0

suggerisco TableProperty serratura o DataContext

+5

Per curiosità ... basato su? I problemi di threading sono così complessi che ** qualsiasi ** reazione istintiva dovrebbe essere evitata, e qualsiasi raccomandazione dovrebbe essere una: basata su prove, e b: includere il ragionamento –

+0

@Marc Gravell, grazie :-) –

+0

in realtà era una cosa seria punto che stavo cercando di fare lì ... –

3

Penso che si sta chiamando diversi oggetti. Non c'è alcuna indicazione sul tuo screenshot che stai prendendo valori da thread diversi. Anche l'uso di syncroot non statico non è una buona idea, perché potrebbe causare casi come il tuo. Hai una ragione molto forte per non averla statica?

+0

@Elastep Concordo sul fatto che la prova di essere lo stesso oggetto sia piuttosto carente - da qui la mia richiesta di ulteriori informazioni come un commento (senza risposta) ... –

+0

Ha bisogno di essere non statico perché ci saranno più istanze dell'oggetto che non devono si bloccano a vicenda. Questo codice viene chiamato da un test unitario e in tal caso c'è solo un'istanza. –

Problemi correlati