2009-11-14 17 views
25

Eventuali duplicati:
How should I unit test threaded code?Unità test in tempo reale/software concorrente

Il test delle unità classico è fondamentalmente solo mettendo in x ed y attesa, e automatizza tale processo. Quindi è buono per testare qualsiasi cosa che non implichi il tempo. Ma poi, la maggior parte dei bug non banali che ho incontrato hanno avuto qualcosa a che fare con il tempismo. I thread corrompono i dati degli altri o causano deadlock. Il comportamento non deterministico si verifica - in una corsa su milioni. Roba difficile.

Esiste qualcosa di utile per le parti di "unit test" di sistemi concatenati multithreading? Come funzionano questi test? Non è necessario gestire a lungo l'argomento di questo test e variare l'ambiente in modo intelligente, per diventare ragionevolmente sicuro che funzioni correttamente?

risposta

11

Gran parte del lavoro che faccio in questi giorni riguarda sistemi multi-thread e/o distribuiti. La maggior parte dei bug riguarda errori di tipo "succede prima", in cui lo sviluppatore assume (erroneamente) che l'evento A si verificherà sempre prima dell'evento B. Ma ogni 1000000a volta il programma viene eseguito, l'evento B viene eseguito per primo e ciò causa imprevedibile comportamento.

Inoltre, non esistono strumenti validi per rilevare i problemi di temporizzazione o persino il danneggiamento dei dati causato dalle condizioni di gara. Strumenti come Helgrind e drd del toolkit Valgrind funzionano alla grande per programmi banali, ma non sono molto utili per diagnosticare sistemi complessi e di grandi dimensioni. Per prima cosa, segnalano falsi positivi abbastanza frequentemente (in particolare Helgrind). Inoltre, è difficile rilevare determinati errori durante l'esecuzione con Helgrind/drd semplicemente perché i programmi in esecuzione su Helgrind funzionano quasi 1000 volte più lentamente e spesso è necessario eseguire un programma per un periodo di tempo pari a per riprodurre la condizione di gara. Inoltre, dal momento che girare con Helgrind cambia totalmente i tempi del programma, potrebbe essere impossibile riprodurre un certo problema di temporizzazione. Questo è il problema con problemi di temporizzazione sottili; sono quasi Heisenbergiani nel senso che alterare un programma per rilevare i problemi di temporizzazione può oscurare il problema originale.

Il fatto triste è che la razza umana non è ancora adeguatamente preparata per gestire software complesso e concorrente. Quindi, sfortunatamente, non esiste un modo semplice per testare l'unità. In particolare per i sistemi distribuiti, è necessario pianificare attentamente il programma utilizzando Lamport's happens-before diagrams per identificare l'ordine necessario degli eventi nel programma. Ma alla fine, non puoi veramente sfuggire al test delle unità a forza bruta con input variabili casualmente. Aiuta anche a variare la frequenza di commutazione del contesto dei thread durante il test unitario, ad es. esegue un altro processo in background che richiede solo cicli della CPU. Inoltre, se si ha accesso a un cluster, è possibile eseguire più test unitari in parallelo, in grado di rilevare i bug molto più rapidamente e risparmiare un sacco di tempo.

+0

Grazie per aver segnalato i diagrammi di Lamport! Esistono questi nuovi linguaggi "concorrenti" che nascondono gran parte della complessità del dominio del tempo utilizzando esclusivamente strutture di dati immutabili (ad esempio, Clojure). Ma non sono ancora soluzioni completamente curative (ancora). –

+0

+1 Semplicemente perché questa frase: "Il fatto triste è che la razza umana non è ancora adeguatamente preparata per affrontare software complessi e concomitanti" mi ha ricordato un certo dottor Cooper. –

4

Non ho mai sentito nulla di ciò che può.

Immagino che se qualcuno dovesse progettarne uno, dovrebbe avere un controllo esatto sull'esecuzione dei thread ed eseguire tutte le possibili combinazioni di stepping dei thread.

Suona come un compito importante, per non parlare delle combinazioni matematici per le discussioni non banali dimensioni quando ci sono una manciata o più di essi ...

Anche se, una rapida ricerca di StackOverflow ... Unit testing a multithreaded application?

+0

Uh, mi chiedo come non l'ho notato. Ad ogni modo, menziona solo uno strumento ed è per .NET, quindi aspettiamo se c'è qualcosa di simile per Java/C/C++. –

5

Se è possibile eseguire i test su Linux, valgrind include uno strumento chiamato helgrind che si propone di rilevare condizioni di competizione e potenziali deadlock nei programmi che utilizzano pthreads; potresti trarre qualche vantaggio dall'esecuzione del tuo codice multithread al di sotto di questo, poiché segnalerà potenziali errori anche se in realtà non si verificano in quella particolare esecuzione di test.

3

Se il sistema testato è abbastanza semplice, è possibile controllare abbastanza bene la concorrenza bloccando le operazioni nei sistemi di prototipazione esterni. Questo blocco può essere fatto ad esempio attendendo l'avvio di qualche altra operazione. Se riesci a controllare tutte le chiamate esterne, questo potrebbe funzionare abbastanza bene implementando diverse sequenze di blocco. Ho provato questo e svela abbastanza bene i bug a livello di blocco se si conoscono bene le possibili sequenze problematiche. E rispetto a molti altri test di concorrenza è abbastanza deterministico. Tuttavia questo approccio non rileva troppo bene le condizioni di gara di basso livello. Di solito vado per il test del carico per trovarli, ma suppongo che non sia esattamente il test delle unità.

Ho visto questi framework di test di concorrenza per .net, presumo che sia solo questione di tempo prima che qualcuno ne scriva uno per Java (si spera).

E non dimenticare la buona lettura del vecchio codice. Uno dei modi migliori per trovare bug di concorrenza è quello di leggere nuovamente il codice ancora una volta dandogli tutta la tua concentrazione.

+0

Sicuramente l'unico modo per ottenere un software corretto al 100% - concomitante o meno - è semplicemente farlo correttamente, pensarci, verificarlo e così via. Nessuno strumento può farlo per te. Ma in pratica, dati i vincoli del mondo reale e che siamo tutti umani, qualsiasi strumento utile è benvenuto quando si lavora in un dominio difficile come un software concorrente. –

1

Forse la risposta è che non dovresti. Nei sistemi concorrenti, potrebbe non esserci sempre una singola risposta deterministica corretta.

Prendete l'esempio di persone che si imbarcano su un treno e scelgono un posto. Stai per finire con risultati diversi ogni volta.

+1

Ma ci sono certamente risposte che non sono corrette, anche se c'è più di una risposta corretta. Se le persone che si imbarcano cominciano a cadere dai sedili del treno, non è un buon risultato :-) –

1

Awaitility è una struttura utile quando è necessario affrontare l'asincronia nei test. Ti permette di aspettare fino a quando qualche stato nel tuo sistema è stato aggiornato. Per esempio:

await().untilCall(to(myService).myMethod(), equalTo(3)); 

o

await().until(fieldIn(myObject).ofType(int.class), greaterThan(1)); 

Ha anche Scala e il supporto Groovy.