Cosa impedisce una condizione di gara su un lucchetto?


24

Comprendo le basi di ciò che sono le corse di dati e come blocchi / mutex / semafori aiutano a prevenirle. Ma cosa succede se hai una "condizione di gara" sul blocco stesso? Ad esempio, due thread diversi, forse nella stessa applicazione, ma in esecuzione su processori diversi, tentano di acquisire un blocco nello stesso momento .

Cosa succede allora? Cosa viene fatto per impedirlo? È impossibile o semplicemente improbabile? O è una vera condizione di gara in attesa di accadere?


Questa domanda è stata posta in precedenza su SO: stackoverflow.com/questions/980521/…
Doc Brown,


Acquisisci un lucchetto per acquisire il lucchetto;) (in altre parole, se il lucchetto ha una condizione di gara, non è implementato correttamente - un blocco è praticamente definito come un costrutto che implementa l'esclusione reciproca)
Tangrs

Hai perso un punto importante su come funzionano i lucchetti. Sono costruiti in modo tale che non è possibile avere una corsa su una serratura, altrimenti sono completamente inutili.
Zane,

Risposte:


36

È impossibile o semplicemente improbabile?

Impossibile. Può essere implementato in diversi modi, ad esempio tramite il confronto e lo scambio in cui l'hardware garantisce l'esecuzione sequenziale. Può essere un po 'complicato in presenza di più core o anche più socket e ha bisogno di un protocollo complicato tra i core, ma tutto questo è curato.


3
Grazie ... Dio ... è gestito in hardware ... (o almeno un livello inferiore a quello che tocchiamo.)
corsiKa

2
@gdhoward Non ci posso credere ... questa risposta mi ha richiesto meno di 5 minuti ed è la terza più votata tra le mie quattrocento risposte (principalmente SO). E probabilmente anche il più corto.
Maaartinus,

1
@maaartinus - A volte corto e dolce lo fa.
Bobson,

17

Studia il concetto di operazioni atomiche "Test and Set".

Fondamentalmente l'operazione non può essere divisa - non è possibile che due cose lo faccia esattamente nello stesso momento. Controllerà un valore, lo imposterà se è chiaro e restituirà il valore com'era durante il test. In un'operazione di blocco, il risultato sarà sempre "lock == TRUE" dopo un test-and-set, l'unica differenza è che è stato impostato o meno all'inizio.

A livello di microcodice in un processore single core, questa è un'istruzione indivisibile ed è facile da implementare. Con processori multipli e multicore, diventa più difficile, ma come programmatori non dobbiamo preoccuparci, poiché è progettato per funzionare da ragazzi davvero intelligenti che fanno il silicio. Fondamentalmente fanno la stessa cosa - fanno un'istruzione atomica che una versione stravagante di test-and-set


2
Fondamentalmente, se l'hardware non è intrinsecamente sequenziale ad un certo livello, avrà un meccanismo che gli consente di rompere i legami che altrimenti potrebbero verificarsi.
Bill Michell,

@BillMichell, avrei dovuto pensarci. In realtà, l'ho fatto; Non sapevo se la mia ipotesi fosse corretta.
Gavin Howard,

2

Basta inserire il codice per entrare nella sezione critica è appositamente progettato in modo che una condizione di gara non violi l'esclusione reciproca.

La maggior parte delle volte vengono utilizzati loop di confronto e set atomici che vengono eseguiti a livello hardware

while(!CompareAndSet(&lock, false, true));//busy loop won't continue until THIS thread has set the lock to true
//critical section
CompareAndSet(&lock, true, false);

In mancanza di ciò, esistono soluzioni software ben studiate per consentire l'esclusione reciproca.


1

Non è possibile che due (o più) thread acquisiscano il blocco contemporaneamente. Esistono alcuni tipi di metodi di sincronizzazione per esempio:

Attesa attiva - blocco spin

pseudocodice:

1. while ( xchg(lock, 1) == 1); - entry protocole

XCHG è un esempio di operazione atomica (esiste sull'architettura x86) che prima imposta un nuovo valore per una variabile "lock" e quindi restituisce il vecchio valore. Atomic significa che non può essere interrotto, nell'esempio sopra riportato tra l'impostazione di un nuovo valore e il ritorno di vecchio. Atomico - risultato deterministico, non importa quale.

2. Your code
3. lock = 0; - exit protocol

Quando il blocco è uguale a 0, un altro thread può accedere alla sezione critica, mentre il loop termina.

Sospensione del thread, ad esempio conteggio del semaforo

Esistono due operazione atomica .Wait()ed .Signal()e abbiamo variabile intera consente di chiamare int currentValue.

Wait():
if (currentValue > 0) currentValue -= 1;
else suspend current thread;

Signal():
If there exists thread suspended by semaphore wake up one of them
Else currentValue += 1;

Ora risolvere il problema della sezione critica è davvero semplice:

pseudocodice:

mySemaphore.Wait();
do some operations - critical section
mySemaphore.Signal();

Di solito l'API del thread di programmazione dovrebbe darti la possibilità di specificare i thread simultanei massimi nella sezione critica dei semafori. Ovviamente ci sono più tipi di sincronizzazione nei sistemi multithread (mutex, monitor, semaforo binario ecc.) Ma si basano su idee sopra. Si potrebbe sostenere che i metodi che usano la sospensione dei thread dovrebbero essere preferiti all'attesa attiva (quindi la CPU non è sprecata) - non è sempre la verità. Quando il thread viene sospeso, ha luogo un'operazione costosa chiamata switch di contesto. Tuttavia è ragionevole quando il tempo di attesa è breve (numero di thread ~ numero di core).

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.