Test di concorrenza / sicurezza del thread

4

Un programma che ho scritto utilizza più thread e credo che il mio programma sia sicuro per i thread, ma come posso davvero saperlo?

Ho letto un certo numero di esempi online e nessuno di loro descrive come testare che il codice sia effettivamente sicuro o meno thread, né ho trovato un modo affidabile per causare un conflitto.

La maggior parte di ciò che ho testato in merito alla concorrenza è stato per lo più incrociato tra le dita e speranze. In effetti ci sono stati alcuni casi in cui il codice sarebbe in conflitto su base regolare, ma cosa posso fare per rassicurare le persone che usano il mio codice e me stesso, che il codice è robusto e affidabile?

    
posta glend 05.06.2015 - 15:17
fonte

4 risposte

1

Non puoi (realisticamente) dimostrare esaurientemente la sicurezza del thread per un linguaggio come Java, ma ci sono modi per rilevare alcuni bug.

Il comportamento ufficiale del tuo programma Java quando viene eseguito su più thread è definito da Capitolo 17 del JLS . L'unico modo per dimostrare che un programma è thread-safe consiste nell'applicare tutte queste regole in un modo teorico, che nella maggior parte degli scenari è troppo complesso.

Ci sono alcuni strumenti di analisi statica che possono aiutare, come FindBugs, ma copriranno solo gli errori più evidenti.

Esistono alcuni strumenti per test di stress come jcstress che eseguiranno il tuo codice molte volte fino a quando non sii sicuro che si comportano come previsto, ma alcuni bug di concorrenza verranno visualizzati solo su hardware / JVM specifici. Ad esempio, il modello di memoria delle CPU x86 è abbastanza strong e non mostrerà mai bug di concorrenza che verrebbero visualizzati su più deboli modelli di memoria .

Allo stesso modo, alcune JVM ottimizzeranno il tuo codice in modo più aggressivo rispetto ad altri e attiveranno più bug in questo modo (ad esempio: hosting variabile).

Alla fine, un percorso ragionevole è una combinazione di:

  • comprensione approfondita del modello di memoria per evitare il maggior numero possibile di insidie in fase di progettazione
  • un buon framework di test (ad es. jcstress) che si esegue su vari computer (mix di SO e CPU) per individuare eventuali problemi rimanenti
risposta data 06.06.2015 - 00:22
fonte
5

Sicurezza del filo significa correttezza di fronte alle molte possibili interazioni esponenziali tra processi concorrenti. Molti bug relativi alla concorrenza si verificano solo in una piccola minoranza di possibili intrecci (ma si verificano ancora nella produzione perché "Uno su un milione è il prossimo martedì"). Inoltre, i test in generale sono migliori nel trovare bug che nel garantire la loro assenza. Questi dati combinati significano che i test del software hanno un tempo molto difficile a dare fiducia per l'assenza di bug di sicurezza del thread.

Esiste molta ricerca per migliorare questo stato, inventando tecniche che trovano in modo più efficiente un interleaving che fa scattare un bug. Tuttavia, il rapporto costi / benefici è ancora piuttosto negativo rispetto al test per altri tipi di bug.

Un approccio più efficace potrebbe essere quello di adottare una disciplina che è garantita per prevenire le corse di dati. Ad esempio, i dati che non sono condivisi tra i thread non causano problemi, e i dati immutabili non possono causare nemmeno la corsa dei dati. Ciò significa utilizzare astrazioni migliori rispetto alla memoria condivisa, tutto-mutabile, con blocchi nudi. Attori, passaggio di messaggi, riduzioni di mappe, operatori di fork-join che non condividono dati (serializzano l'accumulo dei risultati parziali) e altri paradigmi hanno maggiore probabilità di codice corretto. Anche se questi strumenti non riescono a esprimere tutto ciò che desideri parallelizzare, puoi provare a fare il più possibile con loro.

Sfortunatamente, Java il linguaggio non fornisce molti strumenti per applicare tale disciplina, ma usare librerie / framework che li forniscono e magari eseguire controlli di run-time sono meglio di niente. Ci sono anche strumenti (almeno per C ++, non so se esiste qualcosa di simile per Java) che cercano di applicare una disciplina di blocco appropriata associando i dati protetti con il blocco e cercando di assicurarsi che i dati non siano accessibili mentre il blocco non è tenersi.

    
risposta data 05.06.2015 - 15:58
fonte
2

Dimostrare la sicurezza del thread significa dimostrare che ogni possibile interleaving delle istruzioni in due stream opcode porta al risultato corretto. Pertanto, per testare questa proprietà, devi essere in grado di intercalare le istruzioni come desiderato.

In pratica, ciò significa che devi avere un qualche tipo di hook all'interno del codice che viene normalmente eseguito come un'unità. Questo di solito comporta l'aggiunta di ganci di qualche tipo al codice che consentono di avviare e interrompere l'elaborazione in punti ben definiti. Può essere fatto tramite AOP, o tramite riflessione, o ovviamente manualmente; Jaroslav Tulach ha descritto una volta una tecnica interessante in cui sfrutta le affermazioni log4j già presenti nel suo codice commerciale a questo scopo. Ma tutte queste possibilità tendono a essere sforzi elevati. Questo è solo uno dei motivi per cui la sicurezza del thread è rara e il test di sicurezza del thread è ancora più raro.

    
risposta data 05.06.2015 - 15:26
fonte
1

Non ci hai detto che tipo di multi-threading fai, ma presumo che tu faccia multi-threading del tipo di lock, (tu usi la parola chiave synchronized di java,) altrimenti probabilmente non chiederei al domanda.

Dal momento che i test hanno guadagnato terreno nella disciplina dell'ingegneria del software negli ultimi dieci anni circa, il multi-threading del tipo di chiusura è caduto dal favore in proporzione. Questo perché il codice multi-threading del tipo di blocco non può essere realmente testato.

Invece, la linea moderna di pensiero rispetto al multi-threading è preferire meccanismi che eliminino le dipendenze tra i thread. In questo modo, il codice può essere testato correttamente in modo sequenziale ed è (specie) garantito il funzionamento quando viene inserito in thread paralleli, poiché i thread paralleli non dipendono l'uno dall'altro.

Uno strumento molto importante per ottenere questo risultato è message passing . Ciò significa che ogni thread riceve i dati per operare in una coda di messaggi e invia i dati elaborati attraverso un'altra coda di messaggi. I messaggi sono oggetti immutabili, quindi è impossibile che un thread modifichi inavvertitamente il contenuto di un messaggio, causando il danneggiamento in un thread diverso. Essenzialmente, il blocco è ancora utilizzato, ma è localizzato in una sola piccola classe, la classe che implementa la coda dei messaggi effettiva, che si può tranquillamente presumere essere già accuratamente testata e perfettamente funzionante.

Se hai un sacco di dati da elaborare, potresti pensare che raccoglierlo e impacchetterlo in oggetti immutabili e metterlo in coda potrebbe rappresentare un sovraccarico significativo, ma in realtà questo tende a essere sfalsato (a volte notevolmente offset) dal fatto che una volta che un thread ha una porzione di dati da elaborare, può funzionare senza doverlo bloccare continuamente. Vedete, ogni volta che provate a bloccare qualcosa che accade già bloccato da un altro thread, il vostro thread viene posto in uno stato di attesa, che è una penalità enorme per le prestazioni.

    
risposta data 05.06.2015 - 16:34
fonte

Leggi altre domande sui tag