Dovrei occuparmi delle condizioni di gara che quasi sicuramente non ha possibilità di verificarsi?


52

Consideriamo qualcosa come un'applicazione GUI in cui il thread principale sta aggiornando l'interfaccia utente quasi istantaneamente e alcuni altri thread eseguono il polling dei dati sulla rete o qualcosa che garantirà 5-10 secondi per terminare il lavoro.

Ho ricevuto molte risposte diverse per questo, ma alcune persone dicono che se si tratta di una condizione di gara di un'impossibilità statistica, non ti preoccupare affatto, ma altri hanno detto che se c'è anche un 10 -53 % (I bambini non sui numeri, questo è quello che ho sentito) di qualche magia voodoo che si verifica a causa delle condizioni di gara, ottieni sempre / rilascia i blocchi sul thread che ne ha bisogno.

Quali sono i tuoi pensieri? È una buona pratica di programmazione gestire le condizioni di gara in situazioni statisticamente impossibili? o sarebbe del tutto superfluo o addirittura controproducente aggiungere più righe di codice per ostacolare la leggibilità?


21
Quando le persone affermano possibilità del genere, perché nessuno si chiede dell'istruzione della persona dichiarando quel numero? Hai bisogno di un'istruzione formale in statistica prima di poter eseguire il backup con un numero simile.
Pieter B

27
Come fisico, p <1E-140 significa p = 0. Non succederà in questo universo. 0,0000000000000000000000000000000000000000000000000000001% è molto più grande.
Salmo il

15
Assicurarsi che questa condizione gara non può portare a qualcuno di buon grado crash il vostro app. Questa potrebbe essere la causa di un problema di sicurezza.
toasted_flakes

27
Una su un milione di possibilità si verificano nove volte su dieci.
Kaz Dragon,

27
"quasi sicuramente non ha possibilità di verificarsi?" significa che succede nella produzione alle 3 del mattino e molto probabilmente è molto costoso.

Risposte:


137

Se si tratta veramente di un evento 1 su 10 ^ 55, non sarebbe necessario codificarlo. Ciò implicherebbe che se si eseguisse l'operazione 1 milione di volte al secondo, si otterrebbe un bug ogni 3 * 10 ^ 41 anni che è, all'incirca, 10 ^ 31 volte l'età dell'universo. Se la tua applicazione presenta un errore solo una volta ogni trilione di miliardi di miliardi di anni dell'universo, è probabilmente abbastanza affidabile.

Tuttavia, scommetterei fortemente che l'errore non è affatto improbabile. Se riesci a concepire l'errore, è quasi certo che si verificherà almeno occasionalmente, quindi vale la pena codificare correttamente per cominciare. Inoltre, se si codificano correttamente i thread all'inizio in modo che ottengano e rilasci i blocchi in modo appropriato, il codice sarà molto più gestibile in futuro. Non devi preoccuparti quando stai apportando una modifica che devi analizzare nuovamente tutte le potenziali condizioni di gara, ricalcolare le loro probabilità e assicurarti che non si ripresenteranno.


66
Mi viene in mente un commento che ho letto anni fa ma non riesco a trovare ora "Un 1 su un milione di probabilità è di solito martedì prossimo". +1 per dire che è "da nessuna parte vicino a quello improbabile".
Bevan,

2
+1 per la scommessa. Il modo migliore per affrontare le condizioni di gara è liberarsene.
Blrfl,

10
@Bevan "Un 1 su un milione di probabilità è di solito il prossimo martedì" ... a meno che tu non stia giocando una lotteria :)
dasblinkenlight,

22
@dasblinkenlight Ma le probabilità che qualcuno vinca nella maggior parte delle lotterie si avvicinano al 100%. Prevedere chi , ora questa è la sfida.
Bevan,

3
@Bevan: quel commento era esattamente quello che mi passava per la testa quando ho letto la domanda - ecco il riferimento: blogs.msdn.com/b/larryosterman/archive/2004/03/30/104165.aspx
Doc Brown

69

Dal punto di vista del rapporto costi-benefici, è necessario scrivere un codice aggiuntivo solo quando si ottiene un vantaggio sufficiente.

Ad esempio, se la cosa peggiore che accadrebbe se un thread errato "vince la gara" è che le informazioni non verranno visualizzate e l'utente dovrebbe fare clic su "aggiorna", non preoccuparti di proteggerti dalle condizioni della gara: dover scrivere un sacco di codice non vale la pena riparare qualcosa di così insignificante.

D'altra parte, se le condizioni di gara potrebbero comportare trasferimenti di denaro errati tra conti bancari, è necessario proteggersi dalle condizioni di gara, indipendentemente dalla quantità di codice che è necessario scrivere per risolvere questo problema.


20
+1: per fare la distinzione tra "Fallimento che sembra fallimento" e "Fallimento che sembra successo". Le informazioni errate sono molto più serie, a seconda del dominio.
Deworde,

2
+1 fa una grande differenza quali potrebbero essere i risultati delle condizioni di gara.
Concedi il

+1 La conseguenza delle condizioni di gara dovrebbe essere un fattore decisivo nel decidere se affrontarla. Una condizione di gara che potrebbe causare un incidente aereo è molto diversa da una condizione che potrebbe costringere l'utente a riaprire un'applicazione.
colpì il

1
+1: Direi che le conseguenze sono probabilmente ciò che dovresti analizzare e non la probabilità che si verifichi. Se le conseguenze non contano, potresti non dover gestire le condizioni della gara ANCHE se è molto comune.
Leone,

1
Ma non dare per scontato che la correzione automatica di una race condition significhi che devi scrivere più codice. Potrebbe anche significare rimuovere un grosso blocco di codice errato e sostituirlo con un blocco più piccolo di codice corretto.
JesperE,

45

Trovare una condizione di gara è la parte difficile. Probabilmente hai speso quasi tutto il tempo a scrivere questa domanda come ti ci sarebbe voluto per risolverlo. Non è che lo rende molto meno leggibile. I programmatori si aspettano di vedere il codice di sincronizzazione in tali situazioni, e in realtà potrebbero perdere più tempo a chiedersi perché non è presente e se aggiungendolo si corregge il loro bug non correlato.

Per quanto riguarda le probabilità, rimarrai sorpreso. L'anno scorso ho avuto un rapporto sui bug delle condizioni di gara che non riuscivo a riprodurre con migliaia di tentativi automatici, ma un sistema di un cliente lo ha sempre visto. Il valore aziendale di spendere 5 minuti per risolverlo ora, rispetto alla possibile risoluzione di un bug "impossibile" all'installazione di un cliente, rende la scelta un gioco da ragazzi.


1
Anche questo! Evita che altri programmatori riflettano su possibili problemi durante la lettura del codice, facendo ciò che è necessario (anche se è "improbabile" che fallisca).
Casey Kuball,

Il tuo punto è ben preso (le correzioni apportate ora sono più veloci ed economiche di quelle fatte in seguito) tranne per il fatto che non saranno mai solo "5 minuti per risolverlo ora".
iconoclasta il

2
+1 per indicare che la probabilità della condizione di gara dipende probabilmente da molti fattori, quindi anche se sembra improbabile nella tua configurazione, può accadere più frequentemente su un sistema cliente / su un sistema operativo diverso / nella prossima versione ecc.
sleske,

27

Ottieni e rilascia i blocchi. Le probabilità cambiano, gli algoritmi cambiano. È una cattiva abitudine da prendere, e quando qualcosa va storto non devi fermarti e chiederti se hai sbagliato le probabilità ...


6
+1 per cambio algoritmi. In questo momento, quando sei consapevole delle condizioni di gara, le probabilità sono basse. Dopo un anno, quando ti sei dimenticato delle condizioni di gara, puoi apportare una modifica al tuo codice che modifica significativamente i tempi e la probabilità di un bug.
Phil

13

e qualche altro thread sta eseguendo il polling dei dati sulla rete o qualcosa che garantirà 5-10 secondi per terminare il lavoro.

Fino a quando qualcuno non introduce un livello di memorizzazione nella cache per migliorare le prestazioni. All'improvviso quell'altro battistrada finì quasi istantaneamente e le condizioni di gara si manifestano più spesso che no.

Se fosse successo esattamente qualche settimana fa, ci sono voluti circa 2 giorni completi per gli sviluppatori per trovare il bug.

Correggi sempre le condizioni di gara se le riconosci.


8

Semplice vs corretto.

In molti casi, la semplicità supera la correttezza. È un problema di costi.

Inoltre, le condizioni di gara sono cose brutte che tendono a non obbedire a semplici statistiche. Tutto va bene fino a quando qualche altra sincronizzazione apparentemente non correlata fa sì che le tue condizioni di gara si verifichino improvvisamente per la metà del tempo. A meno che non accenda i log o esegua il debug del codice ovviamente.

Un'alternativa pragmatica alla prevenzione di una condizione di gara (che può essere difficile) può essere quella di rilevarla e registrarla (bonus per il fallimento duro e precoce). Se non succede mai, hai perso poco. Se ciò accade effettivamente, hai una solida giustificazione per passare il tempo extra a risolverlo.


1
+1 per la registrazione e fallire presto se risolverlo completamente è troppo complicato.
Martin Ba,

In molti casi, la semplicità supera la completezza. La sincronizzazione non è quasi mai tra questi casi. Quasi sempre tornerà a morderti (o al povero ragazzo incaricato di mantenere il tuo codice) in seguito.
reirab

@reirab Non sono d'accordo. Se si considerano eventi rari, l'errore registrato è conveniente. Un esempio: se l'app del tuo telefono ha un tasso di fallimento 1/100 (crash) se l'utente sta cambiando rete durante una transizione di mese esatta (1/31 23:59:00 -> 2/1 00:00:00), tu probabilmente non ne sentirò mai parlare. Ma allora una possibilità 1/10 ^ 9 di crash sulla connessione su un server è inaccettabile. Dipende.
ptyx,

7

Se la tua condizione di gara è legata alla sicurezza, dovresti sempre programmare per prevenirla.

Un esempio comune sono le condizioni di competizione con la creazione / apertura di file in unix, che possono in alcuni casi portare ad attacchi di escalation di privilegi se il programma con la condizione di competizione è in esecuzione con privilegi più elevati rispetto all'utente che interagisce con esso, come un processo daemon di sistema o peggio ancora, il kernel.

Anche se una condizione di razza ha una probabilità del 10 ^ (- 80) di accadere in modo casuale , può darsi che un determinato attaccante abbia una discreta possibilità di creare tali condizioni deliberatamente e artificialmente.


6

Therac-25!

Gli sviluppatori del progetto Therac-25 erano piuttosto fiduciosi sui tempi tra un'interfaccia utente e un problema relativo all'interfaccia in una macchina XRAY terapeutica.

Non avrebbero dovuto essere.

Puoi saperne di più su questo famoso disastro software vita-e-morte a:

http://www.youtube.com/watch?v=izGSOsAGIVQ

o

http://en.wikipedia.org/wiki/Therac-25

L'applicazione potrebbe essere molto meno sensibile ai guasti rispetto ai dispositivi medici. Un metodo utile è valutare l'esposizione al rischio come il prodotto della probabilità di accadimento e il costo dell'occorrenza nel corso della vita del prodotto per tutte le unità che potrebbero essere prodotte.

Se hai scelto di costruire il tuo codice per durare (e sembra che tu abbia), dovresti considerare la legge di Moore che può facilmente eliminare diversi zeri ogni pochi anni man mano che i computer all'interno o all'esterno del tuo sistema diventano più veloci. Se spedisci migliaia di copie, elimina più zeri. Se gli utenti eseguono questa operazione quotidianamente (o mensilmente) per anni, eliminane alcuni. Se viene utilizzato dove è disponibile la fibra di Google, che cosa succede? Se la spazzatura dell'interfaccia utente raccoglie l'operazione della metà della GUI, ciò influisce sulla gara? Stai usando una libreria Open Source o Windows dietro la tua GUI? Gli aggiornamenti possono influire sui tempi?

Semafori, blocchi, mutex, sincronizzazione delle barriere sono tra i modi per sincronizzare le attività tra i thread. Potenzialmente se non li stai usando, un'altra persona che mantiene il tuo programma potrebbe e quindi ipotizzare piuttosto rapidamente ipotesi sulle relazioni tra le discussioni possono cambiare e il calcolo sulla condizione della razza potrebbe essere invalidato.

Ti consiglio di sincronizzare esplicitamente perché mentre potresti non vederlo mai creare un problema, un cliente potrebbe. Inoltre, anche se la tua condizione di razza non si verifica mai, cosa succede se tu o la tua organizzazione siete chiamati in tribunale per difendere il vostro codice (poiché la Toyota era collegata alla Prius qualche anno fa). Più approfondita è la tua metodologia, migliore sarà il tuo prezzo. Potrebbe essere più bello dire "ci guardiamo da questo caso improbabile come questo ..." piuttosto che dire "sappiamo che il nostro codice fallirà, ma abbiamo scritto questa equazione per mostrare che non accadrà nella nostra vita. Probabilmente. "

Sembra che il calcolo delle probabilità provenga da qualcun altro. Conoscono il tuo codice e li conosci abbastanza per credere che non sia stato commesso alcun errore? Se calcolassi un'affidabilità del 99,9997% per qualcosa, potrei anche ripensare alle mie lezioni di statistica del college e ricordare che non ho sempre ottenuto il 100%, e arretrare di un po 'del percento sulle mie stime di affidabilità personale.


1
+1 per la menzione di Therac-25. Molte lezioni importanti qui.
Stuart segna il

Mentre penso che questa sia una buona risposta, potresti sostenere che il tuo progetto di GUI per hobby non farà sicuramente morire le persone se non riesci a eliminare una condizione di gara.
Marktani,

Non ho molto da obiettare, ma se lo fossi, potrei sostenere che ogni volta che scriviamo codice dovremmo scriverlo nel modo giusto. Se riusciamo a esercitarci a ottenere le condizioni di gara dai nostri progetti di hobby in cui il codice è più semplice e forse siamo l'unico autore, saremo molto più pronti quando affronteremo progetti di lavoro in cui il lavoro di diversi autori deve essere integrato insieme.
Sviluppatore:

4

sarebbe totalmente inutile o addirittura controproducente aggiungere più righe di codice per ostacolare la leggibilità?

La semplicità è buona solo quando è anche corretta. Poiché questo codice non è corretto, i futuri programmatori lo guarderanno inevitabilmente quando cercano un bug correlato.

Indipendentemente dal modo in cui lo gestisci (registrandolo, documentandolo o aggiungendo i blocchi - questo dipende dal costo), risparmierai tempo agli altri programmatori quando guarderai il codice.


3

Ciò dipenderebbe dal contesto. Se è un gioco per iPhone casual, probabilmente no. Il sistema di controllo del volo per il prossimo veicolo spaziale con equipaggio, probabilmente. Tutto dipende da quali sono le conseguenze se il risultato 'cattivo' si verifica misurato rispetto al costo stimato di fissarlo.

Raramente v'è un 'one size fits all' risposta per questi tipi di domande, perché sono non programmano domande, ma invece di economia domande.


3
"Il sistema di controllo del volo per il prossimo veicolo spaziale con equipaggio" DEFINITAMENTE .
Deworde,

probabilmente ... sicuramente ... che sarebbe dipende da chi era nel razzo :-)
GrandmasterB

3

Sì, aspettati l'inaspettato. Ho trascorso ore (nel codice di altre persone ^^) a rintracciare condizioni che non dovrebbero mai accadere.

Cose come avere sempre un altro, avere sempre un valore predefinito nel caso, inizializzare le variabili (sì, davvero .. i bug si verificano da questo), controllare i propri loop per le variabili riutilizzate per ogni iterazione, ecc.

Se sei preoccupato per i problemi di threading in particolare, leggi blog, articoli e libri sull'argomento. Il tema attuale sembra essere dati immutabili.


3

Basta aggiustarlo.

Ho visto esattamente questo. Un thread riesce a inviare una richiesta di rete a un server che esegue una ricerca di database complessa e risponde prima che l'altro thread abbia raggiunto la riga di codice successiva. Succede.

Qualche cliente da qualche parte deciderà un giorno di eseguire qualcosa che impiega tutto il tempo della CPU per il thread "veloce" lasciando il thread lento in esecuzione, e te ne pentirai :)


1

Se hai riconosciuto una condizione di gara improbabile, almeno documentala nel codice!

EDIT: dovrei aggiungere che lo risolverei se possibile, ma al momento della stesura di cui sopra nessuna altra risposta ha detto esplicitamente almeno di documentare il problema nel codice.


1
Sì, e almeno prova a rilevarlo e registralo se succede. IMHO è perfettamente bene non evitare ogni errore. Ma almeno fai sapere a qualcuno che è successo e che la tua supposizione che non sarebbe stata sbagliata.
Steve Bennett,

0

Penso che se sai già come e perché potrebbe accadere, potresti anche affrontarlo. Cioè se non occupa una quantità abbondante di risorse.


0

Tutto dipende da quali siano le conseguenze di una condizione di razza. Penso che le persone che hanno risposto alla tua domanda siano corrette per la loro linea di lavoro. Il mio è il motore di configurazione del router. Per me, le condizioni di gara rendono i sistemi fermi, corrotti o non configurati, anche se affermano che ha avuto successo. Uso sempre i semafori per router in modo da non dover pulire nulla a mano.

Penso che parte del mio codice GUI sia ancora soggetto a condizioni di gara in modo tale che un utente possa ricevere un errore perché si è verificata una condizione di gara, ma non avrei tali possibilità se ci fosse la possibilità di corruzione dei dati o comportamento scorretto del applicazione dopo tale evento.


0

Stranamente, ho riscontrato questo problema di recente. Non mi ero nemmeno reso conto che una condizione di razza fosse possibile nelle mie circostanze. La condizione di gara si presentava solo quando i processori multi-core sono diventati la norma.

Lo scenario era più o meno così. Un driver di dispositivo ha generato eventi che il software può gestire. Il controllo doveva tornare al driver del dispositivo il più presto possibile per evitare un timeout sul dispositivo. Per garantire ciò, l'evento è stato registrato e messo in coda in un thread separato.

Receive event from device:
{
    Record event details.
    Enqueue event in the queuing thread.
    Acknowledge the event.
}

Queueing thread receives an event:
{
    Retrieve event details.
    Process event.
    Send next command to device.
}

Questo ha funzionato benissimo per anni. Quindi improvvisamente fallirebbe in alcune configurazioni. Si scopre che il thread di accodamento ora stava funzionando veramente in parallelo al thread di gestione degli eventi, piuttosto che condividere il tempo di un singolo processore. È riuscito a inviare il comando successivo al dispositivo prima che l'evento fosse riconosciuto, causando un errore fuori sequenza.

Dato che ha interessato solo un cliente in una configurazione, ho vergognosamente inserito un Thread.Sleep(1000)problema. Non c'è stato un problema da allora.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.