Come posso testare il codice multi-threaded dell'unità? [duplicare]

32

Ci sono modi per testare il tuo codice multi-threaded per condizioni di gara e deadlock?

Per vedere se stanno eseguendo il modo in cui dovrebbero essere ...

    
posta Tom Wijsman 02.10.2010 - 13:37
fonte

8 risposte

19

CHESS , un progetto di Microsoft Research. Citando il loro sito:

CHESS is a tool for finding and reproducing Heisenbugs in concurrent programs. CHESS repeatedly runs a concurrent test ensuring that every run takes a different interleaving. If an interleaving results in an error, CHESS can reproduce the interleaving for improved debugging. CHESS is available for both managed and native programs.

Aggiornamento (23/09/2015): per C, C ++ e Go, puoi utilizzare ThreadSanitizer .

    
risposta data 02.10.2010 - 16:05
fonte
10

Valgrind ha Helgrind che aiuta davvero. Non solo aiuta a indicare le gare che potrebbero portare alla fame o al deadlock, il leggero rallentamento del profilo del programma a volte espone le razze che potrebbero non essere viste altrimenti.

Quindi, anche se vai in commando con un qualche tipo di metodo lock free, aiuta ancora:)

Tuttavia, è POSIX centrico. Viene fornito con intestazioni che rendono facilmente semplici librerie di test di unità come TAP consapevole che è in esecuzione, il che è anche molto utile. Ad esempio, potresti avere un thread che normalmente non bloccherebbe quando provi ad acquisire un blocco, procedi e blocca (forse in modo casuale), solo per simulare la fame.

    
risposta data 24.11.2010 - 18:04
fonte
5

Non ricordo esattamente i dettagli, ma questa è l'idea generale. E l'ho fatto solo una volta, ma quello che ho fatto è stato separare il codice di rientro dal codice che esegue l'attività, utilizzando un'interfaccia per essere in grado di prendere in giro la classe di attività.

Poi ho progettato il mio mock-up per poter bloccare una chiamata in modo da sapere che il thread si trova nella sezione critica, quindi richiamarlo di nuovo e verificare che sia in attesa, prima di rilasciare il primo thread e terminare in modo pulito .

Qualcosa del genere.

Non sono sicuro che funzioni per scenari più complessi, ma aiuta a preservare il comportamento durante i refactoring.

    
risposta data 24.11.2010 - 14:23
fonte
2

Al JAOO / GOTO quest'anno ho visto questa presentazione:

link

Il trucco è quello di modellare ciò che la tua applicazione hairball dovrebbe fare, in termini di passaggi di invocazione così come le operazioni effettive sulla tua applicazione. Il software John Hughes quindi esegue sistematicamente numerose permutazioni dei passaggi di chiamata ripetutamente in parallelo e verifica successivamente che lo stato dell'applicazione corrisponda allo stato del modello. Se viene rilevato un errore, il software sa come ridurre i passaggi al caso minimo che produce l'errore.

Ha dimostrato dal vivo come catturare diversi bug nelle librerie di base di Erlang che erano in agguato da 15 anni e occasionalmente segnalato, ma nessuno riusciva a capire da dove provenissero e quindi a risolvere il problema. Con i casi minimi riportati dal software, il manutentore della libreria è stato in grado di correggere ogni bug entro un giorno .

È stato così impressionante.

John Hughes vende questo software attraverso la sua azienda.

    
risposta data 24.11.2010 - 15:32
fonte
2
  1. I test con risultati non riproducibili sono inutili. Questo esclude test completamente casuali, ma lascia i test generati da sequenze pseudo-casuali.
  2. Ogni attore in un ambiente concorrente ha componenti algoritmici o altrimenti non concorrenti che possono essere testati con metodi convenzionali. Dopo averli testati, gli eventuali errori rimanenti devono trovarsi nella logica della concorrenza.
  3. Gli eventi in un sistema concorrente sono sempre di fatto una sequenza lineare di eventi. Se viene utilizzata una precisione sufficiente per misurare il tempo, non si verificano eventi "allo stesso tempo". Ciò significa che gli attori di un sistema concorrente possono essere testati generando eventi in sequenza. Catturare la sequenza di eventi attorno al tempo di errore di un sistema concorrente fornisce i casi di test richiesti.
  4. Il codice che fornisce agli attori la vivacità (thread) è più spesso che non fornito dal sistema operativo o dalle librerie di sistema. È lecito ritenere che detto codice non debba essere testato. Il codice responsabile della comunicazione e della sincronizzazione viene normalmente scritto dal programmatore delle applicazioni. Quel codice può essere testato senza richiamare il codice di sistema, cioè senza avviare alcun thread.
  5. Le condizioni al contorno nel codice algoritmico (coda vuota) spesso richiedono la gestione nel codice di sincronizzazione, e questo è un buon obiettivo per il test.
  6. La definizione dei proxy attorno al codice di sistema (t.wait ()) consente l'utilizzo di stub / mock delle funzionalità durante il test.
risposta data 24.11.2010 - 17:03
fonte
1

Puoi provare il mio Relacy Race Detector . È progettato per verificare con cura e precisione gli algoritmi di sincronizzazione come code produttore-consumatore e contenitori simultanei, ma non è molto adatto per la verifica di interi programmi. Tuttavia, forse è una buona idea distribuire comunque la sincronizzazione e i mutex su un programma, ma concentrare la sincronizzazione in componenti specializzati (che possono essere verificati con Relacy).

    
risposta data 29.01.2011 - 12:14
fonte
0

Non è facile, ma fondamentalmente l'unico modo è chiamare il codice multi-thread simultaneamente da più thread e cambiare i tempi e ordinare a caso giocando con% casualeThread.sleep() e Thread.yield() chiamate ( assumendo Java).

Ci sono anche strumenti pronti disponibili (come TestNG) che fanno qualcosa come descritto sopra, ma non sono ancora molto maturi, per quanto ne so.

    
risposta data 05.10.2010 - 10:09
fonte
0

Non un severo test unitario, ma un controllo di runtime che mi ha aiutato con alcuni test intermittenti in errore. È veloce e amp; sporco, ma ha funzionato.

Quando viene concesso un mutex, tengo traccia di quale thread ha. Tutte le richieste mutex hanno un timeout di trenta secondi, dopo di che gridano deadlock.

Posso quindi usare l'elenco dei mutex concessi per vedere quale thread ha il blocco mutex, e perché per così tanto tempo. Nei miei casi finora è perché è stato bloccato su qualcos'altro, quindi posso quindi sistemare quella situazione.

Questo ha funzionato per me perché i miei mutex hanno una classe wrapper multipiattaforma che semplifica l'inserimento della registrazione e del timeout. Sapevo anche abbastanza sull'applicazione per sapere che non avrebbe mai dovuto bloccare un mutex per 30 secondi.

Potrebbe non essere completamente generico, ma consente di risparmiare un sacco di debug per circa un paio d'ore di programmazione. L'overhead è trascurabile e può essere solo di debug-build.

Vedrò di estenderlo per registrare sequenze di richieste mutex annidate, e vedere se vi sono potenzialmente induzioni di deadlock (ad esempio un thread blocca A poi B e un altro blocca B poi A) piuttosto che solo un deadlock che induce, ma finora è stato un grande vantaggio per uno sforzo banale.

    
risposta data 29.01.2016 - 10:45
fonte

Leggi altre domande sui tag