Non sono a conoscenza di un articolo o di un libro che risolva quello che stai cercando, quindi ecco le mie "lezioni apprese" da 12 anni di debug multithreading su Windows (entrambi non gestiti e gestiti).
Come ho affermato nel mio commento, la maggior parte del mio "debug multithreading" viene effettivamente eseguita tramite una revisione manuale del codice, alla ricerca di questi problemi.
deadlock e corrotto Shared Stato
documento lock hierarchies (sia l'ordine e che stato proteggono in comune), e assicurano che siano coerenti. Questo risolve la maggior parte dei problemi di deadlock e problemi di stato condiviso corrotti.
(Nota: il collegamento sopra per "gerarchie di blocco" si riferisce a un articolo del Dr. Dobbs di Herb Sutter, che ha scritto un'intera serie di articoli Effective Concurrency che consiglio vivamente).
Altro su deadlock
Utilizzare RAII for all synchronization. Ciò garantisce che i blocchi vengano rilasciati in caso di eccezioni. Preferisci la frase "lock" per provare/finalmente.
(Si noti che RAII in .NET dipende da IDisposable
, non Finalize
e presuppone che il codice client utilizzi correttamente un blocco using
).
fame
Rimuovere eventuali modifiche di priorità di thread. La corretta definizione delle priorità è in realtà un po 'contro-intuitiva: è meglio dare al thread più lavoro per fare una priorità più bassa, e dare priorità maggiori ai thread che sono vincolati all'I/O (incluso il thread dell'interfaccia utente).Dal momento che Windows lo fa automaticamente (vedi Windows Internals), non c'è davvero alcun motivo per cui il codice sia coinvolto.
In generale
Rimuovere tutto il codice senza blocchi che è stato scritto in-house. Quasi certamente contiene bug sottili. Sostituirlo con .NET 4 lock-free collections e synchronization objects o modificare il codice in base al blocco.
Utilizzare concetti di livello superiore per la sincronizzazione. Il Task Parallel Library e unified cancellation in .NET 4 Rimuovere praticamente qualsiasi esigenza per l'utilizzo diretto di ManualResetEvent
, Monitor
, Semaphore
, ecc
concetti Usa più alto livello per la parallelizzazione. Lo TPL and PLINQ in .NET 4 dispone di algoritmi di autobilanciamento integrati completi di partizioni intelligenti e code di work-stealing per fornire automaticamente una parallelizzazione ottimale. Per i rari casi in cui la parallelizzazione automatica non è ottimale, sia TPL che PLINQ espongono un numero enorme di manopole modificabili (schemi di partizionamento personalizzati, flag di funzionamento di lunga durata, ecc.).
C'è un'altra tecnica che ho trovato utile per qualsiasi classe che ha i suoi metodi chiamati da diversi thread: documenta quali metodi eseguire su quali thread. Di solito, questo viene aggiunto come commento all'inizio del metodo. Assicurati che ogni metodo venga eseguito solo in un contesto di thread noto (ad es. "Su un thread UI" o "su un thread ThreadPool" o "sul thread in background dedicato"). Nessuno dei metodi dovrebbe dire "su qualsiasi thread" a meno che tu non stia scrivendo una classe di sincronizzazione (e se stai scrivendo una classe di sincronizzazione, chiediti se davvero dovresti farlo).
Infine, assegna un nome alle discussioni. Questo aiuta a distinguerli facilmente quando si usa il debugger VS. .NET supporta questo tramite la proprietà .
Ci sono molti strumenti interessanti, ma non trascurare il semplice approccio della semplice lettura del codice. Ho fatto il debugging multithreading per anni, ed è uno dei metodi più efficaci. –
Vero. Funziona piuttosto bene. Sto osservando più tecniche ("come" questo) di strumenti. – Mau
+1 @Stephen Veramente vero. –