Che cosa cerchi quando esegui il debug di deadlock?

23

Recentemente ho lavorato a progetti che utilizzano pesantemente il threading. Penso di essere OK nel progettarli; usa il più possibile il design stateless, blocca l'accesso a tutte le risorse di cui ha bisogno più di un thread, ecc. La mia esperienza nella programmazione funzionale lo ha aiutato immensamente.

Tuttavia, leggendo il codice del thread di altre persone, mi confondo. Sto mettendo a punto una situazione di stallo in questo momento, e poiché lo stile e il design della codifica sono diversi dal mio stile personale, sto attraversando un momento difficile nel vedere potenziali condizioni di deadlock.

Che cosa cerchi quando esegui il debug dei deadlock?

    
posta Michael K 26.01.2011 - 15:56
fonte

7 risposte

23

Se la situazione è una situazione di stallo reale (cioè due thread contengono due blocchi diversi, ma almeno un thread richiede un blocco che l'altro thread contiene), è necessario prima abbandonare tutte le pre-concezioni di come il blocco dei thread di ordine. Non assumere nulla. Potresti voler rimuovere tutti i commenti dal codice che stai guardando, poiché tali commenti potrebbero indurti a credere a qualcosa che non è vero. È difficile enfatizzare abbastanza: non assumere nulla.

Successivamente, determina quali blocchi vengono trattenuti mentre un thread tenta di bloccare qualcos'altro. Se è possibile, assicurarsi che un thread si sblocchi in ordine inverso rispetto al blocco. Ancora meglio, assicurati che un thread abbia un solo blocco alla volta.

Lavorare scrupolosamente attraverso l'esecuzione di un thread ed esaminare tutti gli eventi di blocco. Ad ogni blocco, determina se un thread contiene altri blocchi e, in tal caso, in quali circostanze un altro thread, eseguendo un percorso di esecuzione simile, può accedere all'evento di blocco preso in considerazione.

È certamente possibile che non troverai il problema prima che tu finisca il tempo o i soldi.

    
risposta data 26.01.2011 - 17:00
fonte
11
  1. Come altri hanno già detto ... se puoi ottenere informazioni utili per la registrazione, prova prima, perché è la cosa più facile da fare.

  2. Identifica i blocchi coinvolti. Cambia tutti i mutex / semafori che aspettano per sempre le attese temporizzate ... qualcosa di ridicolmente lungo come 5 minuti. Registra l'errore quando scade. Questo almeno ti indicherà la direzione di uno dei lucchetti coinvolti nel problema. A seconda della variabilità dei tempi potresti essere fortunato e trovare entrambi i blocchi dopo alcune corse. Utilizzare le condizioni / condizioni di errore della funzione per registrare una traccia di pseudo stack dopo che l'attesa programmata non riesce a identificare come ci si è arrivati in primo luogo. Questo dovrebbe aiutarti a identificare il thread che è coinvolto nel problema.

  3. Un'altra cosa che potresti provare è creare una libreria wrapper attorno ai tuoi servizi mutex / semaforo. Tieni traccia di quali thread hanno ciascun mutex e quali thread sono in attesa sul mutex. Costruisci un thread del monitor che controlla la durata del blocco dei thread. Attivare su una ragionevole durata e scaricare le informazioni di stato che si stanno monitorando.

A un certo punto, sarà necessaria la semplice ispezione del codice precedente.

    
risposta data 26.01.2011 - 18:17
fonte
6

Il primo passo (come dice Péter) è il logging. Sebbene nella mia esperienza questo sia spesso problematico. In un'elaborazione parallela pesante questo spesso non è possibile. Ho dovuto eseguire il debug di qualcosa di simile con una rete neurale una volta, che elaborava 100k di nodi al secondo. L'errore si è verificato solo dopo diverse ore e anche una sola riga di output ha rallentato così tanto le cose, che ci sarebbero voluti giorni. Se la registrazione è possibile, concentrati meno sui dati, ma più sul flusso del programma, finché non sai in quale parte succede. Basta una semplice riga all'inizio di ogni funzione e se riesci a trovare la funzione giusta, suddividila in blocchi più piccoli.

Un'altra opzione è la rimozione di parti del codice e dei dati per localizzare il bug. Forse anche scrivere un piccolo programma che prende solo alcune delle classi e che esegue solo i test più elementari (sempre in alcuni thread ovviamente). Rimuovi tutto ciò che è collegato alla GUI, ad esempio qualsiasi output relativo allo stato di elaborazione effettivo. (Ho trovato che l'interfaccia utente è la fonte del bug abbastanza spesso)

Nel tuo codice cerca di seguire il completo flusso logico di controllo tra l'inizializzazione del blocco e il suo rilascio. Un errore comune potrebbe essere bloccare all'inizio di una funzione, sbloccare alla fine, ma avere una dichiarazione di ritorno condizionale da qualche parte nel mezzo. Le eccezioni potrebbero impedire anche il rilascio.

    
risposta data 26.01.2011 - 16:22
fonte
3

I miei migliori amici sono stati dichiarazioni stampa / registro in posti interessanti all'interno del codice. Questi di solito mi aiutano a capire meglio cosa sta realmente accadendo all'interno dell'app, senza interrompere il tempo tra i diversi thread, il che potrebbe impedire la riproduzione del bug.

Se fallisce, il mio unico metodo rimanente è fissare il codice e provare a costruire un modello mentale dei vari thread e interazioni, e cercare di pensare a possibili modi folli per ottenere ciò che apparentemente è successo :-) Ma io non mi considero un deadlock-slayer molto esperto. Spero che altri possano dare idee migliori, dalle quali posso imparare anche: -)

    
risposta data 26.01.2011 - 16:00
fonte
3

Prima di tutto, cerca di ottenere l'autore di quel codice. Probabilmente avrà l'idea di ciò che ha scritto. anche se voi due non riuscite a individuare il problema semplicemente parlando, Almeno potete sedervi con lui per individuare la parte del deadlock, che sarà molto più veloce di capire il suo codice senza aiuto.

In caso contrario, come ha detto Péter Török, la registrazione è probabilmente il modo. Per quanto ne so, Debugger ha fatto un brutto lavoro in ambiente multi-threading. prova a localizzare dov'è il lucchetto, prendi un insieme di risorse in attesa e in quali condizioni si verifica la condizione di competizione.

    
risposta data 26.01.2011 - 16:12
fonte
0

Questa domanda mi attrae;) Prima di tutto, considera la tua fortuna dato che sei riuscito a riprodurre il problema in modo coerente in ogni corsa. Se ricevi sempre la stessa eccezione con lo stesso stacktrace, allora dovrebbe essere abbastanza semplice. In caso contrario, non fidarsi troppo dello stacktrace, ma semplicemente monitorare l'accesso agli oggetti globali e le relative modifiche di stato durante l'esecuzione.

    
risposta data 26.01.2011 - 19:37
fonte
0

Se devi eseguire il debug dei deadlock, sei già nei guai. Come regola generale, usa le serrature per il minor tempo possibile o, se possibile, non del tutto. Qualsiasi situazione in cui si prende un lucchetto e poi si passa al codice non banale dovrebbe essere evitata.

Dipende naturalmente dal tuo ambiente di programmazione, ma dovresti guardare a cose come code sequenziali che ti permettono di accedere a una risorsa solo da un singolo thread.

E poi c'è una vecchia ma infallibile strategia: Assegna un "livello" a ciascun blocco, a partire dal livello 0. Se prendi un livello 0, non ti è permesso nessun altro blocco. Dopo aver preso un blocco di livello 1, puoi prendere un blocco di livello 0. Dopo aver preso un blocco di livello 10, puoi bloccare i blocchi a livello 9 o inferiore ecc.

Se ritieni che ciò sia impossibile, devi correggere il codice perché ti troverai in deadlock.

    
risposta data 28.09.2016 - 19:38
fonte

Leggi altre domande sui tag