2011-09-16 9 views
6

Devo scrivere un test unitario che provoca una condizione di gara, così posso verificare se ho probabilmente risolto il problema in seguito. Il problema è che la condizione di gara si verifica solo molto raramente, forse perché il mio computer ha solo due core.Provocazione di una condizione di competizione in Java

Il codice è simile al seguente:

class MyDateTime { 
    String getColonTime() { 
    // datetime is some kind of lazy caching variable declared somewhere(does not matter) 
    if (datetime == null) { 
     initDateTime(); //Uses lazy to initlialize variable, takes some time 
    } 
    // Colon time stores hh:mm as string 
    if (datetime.colonTime == null) { 
     StringBuilder sb = new StringBuilder(); 
     //Now do some steps to build the hh:mm string 
     //... 
     //set colon time 
     datetime.colonTime = sb.toString(); 
    } 
    return datetime.colonTime; 
    } 
} 

Spiegazione: initDateTime assegna una nuova istanza per dateTime, Perciò, datetime.colonTime è nullo in seguito (come vogliamo inizializzare pigro, come ho detto prima). Ora se il thread A entra nel metodo e quindi lo scheduler lo arresta poco prima che possa essere avviato initDateTime(). Thread B ora runstColonTime(), vede che datetime è ancora null e lo inizializza. datetime.colonTime è null, quindi il secondo blocco if viene eseguito e datetime.colonTime ottiene il valore di StringBuilder. Se quindi lo scheduler interrompe il thread tra questa riga e l'istruzione return e riprende il thread A, accade quanto segue: Poiché A è stato arrestato appena prima di initDateTime viene chiamato, A chiama ora initDateTime(), che sarà il reset oggetto datetime, impostazione di datetime.colonTime su null. Il thread A quindi entrerà nel secondo se blocco, ma lo scheduler interromperà A prima di datetime.colonTime = sb.toString(); è chiamato. In conclusione, dateTime.colonTime è ancora nullo. Ora lo scheduler riprende B e il metodo restituisce null.

Ho provato a provocare la condizione di competizione avendo un numero di fili di chiamata getColonTime() per una singola istanza (finale) di MyDateTime, ma non riesce solo in alcuni casi rari extreeemly :( Eventuali suggerimenti come scrivere un JUnit "test"?

+3

Si potrebbe provare prima ad utilizzare il debugger per provocare condizioni di competizione. Cioè avvia un thread, prendilo su un punto di interruzione (come tra if) e avvialo un altro - e così via. Dopo aver avuto l'idea di come accade RC (sembra che tu non abbia nulla del genere ora) potresti scrivere un test unitario di successo – pupssman

+0

Non vedo come potresti arrivare al 'return datetime.colonTime; 'e restituisce null. Sei sicuro che non sia un problema con il modo in cui stai costruendo la stringa hh: mm? Forse aggiungi quel codice alla tua domanda in modo che possiamo guardarlo. – Windle

+0

Ho aggiunto qualche spiegazione in più sul perché possa accadere. Devo ammettere che non è molto ovvio – user3001

risposta

4

Si potrebbe guardare Thread Weaver, o ci possono essere altri quadri per testare il codice multi-thread. Non l'ho usato, ma il Users' Guide sembra come se fosse progettato esattamente per questo tipo di test.

+0

L'ho già visto, ma non so quanto sia adatto. Esperienze con questo, chiunque? – user3001

+0

Ho provato il thread-weaver. Funziona alla grande per i test se sai dove si trova il problema. Tuttavia, non è possibile eseguire N x N test su linee di un metodo (ho chiesto nel gruppo di discussione). – user3001

7

Come accennato, le condizioni di gara sono estremamente difficili da riprodurre in modo coerente Tuttavia, la legge delle medie è dalla tua parte. Se crei un test che pensi di fallire, forse uno su cento, e quindi fallo succedere migliaia di volte, probabilmente otterrai l'errore in modo abbastanza coerente nel tuo vecchio codice. Quindi, in linea con i principi di TDD, dovresti iniziare con il codice come prima, fare un test itera abbastanza volte fallire coerentemente con il vecchio codice, quindi cambia al tuo nuovo codice e assicurati che non fallisca.

+0

È possibile avere una buona probabilità di fallimento del test, ma maggiore è la probabilità che richiedi più tempo sarà necessario per eseguire il test. I test unitari dovrebbero essere veloci. (Ma non ho un'idea migliore.) –

0

So che questo post è piuttosto vecchio ma sto affrontando una situazione simile. Quello che tendo a fare è favorire la condizione della corsa con il sonno.

Nel tuo caso, vorrei fare qualcosa di simile

class MyDateTime { 
     String getColonTime() throws InterruptedException{ 
      if (datetime == null) { 
       Thread.sleep(new Random().nextInt(100); //Wait to enhance the chances that multiple threads enter here and reset colonTime. 
       initDateTime(); 
      } 
      Thread.sleep(new Random().nextInt(100); //Wait to enhance the chances that colonTime stays null for a while. 
      if (datetime.colonTime == null) { 
       StringBuilder sb = new StringBuilder(); 
       datetime.colonTime = sb.toString(); 
      } 
      Thread.sleep(new Random().nextInt(100); //Wait to favour reset of colonTime by another thread in the meantime. 
      return datetime.colonTime; 
     } 
    } 

Ma è chiaro che questo diventa disordinato abbastanza rapidamente. Vorrei che ci fosse un modo per forzare lo schedulatore a esplorare tutti i percorsi dati alcuni "punti di rottura".

Poiché il post è un po 'vecchio, mi chiedevo se hai trovato buoni modi per testare le condizioni di gara in Java. Qualche consiglio da condividere?

Grazie

+1

Come ho detto nella risposta accettata, Thread Weaver era un ottimo strumento e immagino ci siano ora altri. Ad esempio, IntelliJ ha ricevuto il supporto per il debug multithreading se lo ricordo correttamente. Anche le statistiche sono dalla tua parte: se ripeti un test abbastanza spesso, sicuramente colpirai la condizione di gara una volta ogni tanto :) Vedi anche https://github.com/junit-team/junit4/wiki/Multithreaded-code -e-concorrenza – user3001

Problemi correlati