¿Qué impide una condición de carrera en una cerradura?

24

Entiendo los conceptos básicos de qué son las carreras de datos y cómo los bloqueos / mutexes / semáforos ayudan a prevenirlos. Pero, ¿qué sucede si tienes una "condición de carrera" en la cerradura misma? Por ejemplo, dos subprocesos diferentes, tal vez en la misma aplicación, pero que se ejecutan en procesadores diferentes, intentan adquirir un bloqueo al mismo tiempo .

¿Qué pasa entonces? ¿Qué se hace para evitar eso? ¿Es imposible o simplemente improbable? ¿O es una condición de carrera real esperando que suceda?

Gavin Howard
fuente
Esta pregunta se hizo antes en SO: stackoverflow.com/questions/980521/…
Doc Brown
y una pregunta relacionada aquí en P.SE: programmers.stackexchange.com/questions/228827/…
ratchet freak
Adquieres un candado para adquirir el candado;) (en otras palabras, si tu candado tiene una condición de carrera, no se implementa correctamente; un candado se define como una construcción que implementa la exclusión mutua)
Tangrs
Te perdiste un punto importante en cómo funcionan las cerraduras. Están construidos de tal manera que no es posible tener una carrera en una cerradura, de lo contrario son completamente inútiles.
Zane

Respuestas:

36

¿Es imposible o simplemente improbable?

Imposible. Se puede implementar de diferentes maneras, por ejemplo, a través de Comparar e intercambiar, donde el hardware garantiza la ejecución secuencial. Puede complicarse un poco en presencia de múltiples núcleos o incluso múltiples sockets y necesita un protocolo complicado entre los núcleos, pero todo esto se soluciona.

maaartinus
fuente
3
Gracias a Dios ... ... se maneja en el hardware ... (o, al menos, un nivel más bajo que tocamos.)
corsiKa
2
@gdhoward No puedo creerlo ... esta respuesta me tomó menos de 5 minutos y es la tercera más votada entre mis cuatrocientas respuestas (principalmente SO). Y probablemente también el más corto.
maaartinus
1
@maaartinus: corto y dulce a veces lo hace.
Bobson
17

Estudie el concepto de operaciones atómicas de "prueba y configuración".

Esencialmente, la operación no se puede dividir: no es posible que dos cosas lo hagan exactamente al mismo tiempo. Verificará un valor, lo establecerá si está claro y devolverá el valor como estaba cuando se realizó la prueba. En una operación de bloqueo, el resultado siempre será "lock == TRUE" después de un test-and-set, la única diferencia es si se estableció o no al inicio.

A nivel de microcódigo en un procesador de núcleo único, esta es una instrucción indivisible y fácil de implementar. Con procesadores múltiples y multinúcleo, se vuelve más difícil, pero como programadores no tenemos que preocuparnos por eso, ya que está diseñado para funcionar por los tipos realmente inteligentes que hacen el silicio. Esencialmente hacen lo mismo: hacer una instrucción atómica que una versión elegante de probar y configurar

Mattnz
fuente
2
Básicamente, si el hardware no es intrínsecamente secuencial en algún nivel, tendrá un mecanismo que le permitirá romper los lazos que de otro modo podrían ocurrir.
Bill Michell
@BillMichell, debería haber pensado en eso. En realidad lo hice; Simplemente no sabía si mi suposición era correcta.
Gavin Howard
2

Simplemente ponga el código para entrar en la sección crítica está especialmente diseñado para que una condición de carrera no viole la exclusión mutua.

La mayoría de las veces se utilizan bucles de comparación y configuración atómica que se ejecutan a nivel de 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);

En ausencia de eso, existen soluciones de software bien estudiadas para permitir la exclusión mutua.

monstruo de trinquete
fuente
1

No es posible que dos (o más) subprocesos adquieran bloqueo al mismo tiempo. Hay pocos tipos de métodos de sincronización, por ejemplo:

Espera activa - bloqueo de giro

Pseudocódigo:

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

XCHG es un ejemplo de operación atómica (existe en la arquitectura x86) que primero establece un nuevo valor para una variable de "bloqueo" y luego devuelve un valor antiguo. Atómico significa que no se puede interrumpir, en el ejemplo anterior entre establecer un nuevo valor y devolver el antiguo. Atómico - resultado determinista pase lo que pase.

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

Cuando el bloqueo es igual a 0, otro hilo puede ingresar a la sección crítica, mientras el bucle termina.

Hilo suspendido - por ejemplo contando semáforo

Existen dos operación atómica .Wait()y .Signal()y tenemos la variable entera le llaman 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;

Ahora resolver el problema de la sección crítica es realmente fácil:

Pseudocódigo:

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

Por lo general, su API de subproceso de programación debería permitirle especificar subprocesos simultáneos máximos en la sección crítica del semáforo. Obviamente, hay más tipos de sincronización en sistemas multiproceso (mutex, monitores, semáforos binarios, etc.) pero se basan en las ideas anteriores. Se podría argumentar que los métodos que utilizan la suspensión de subprocesos deberían preferirse a la espera activa (por lo que no se desperdicia la CPU), no siempre es la verdad. Cuando se suspende el subproceso, se realiza una operación costosa llamada cambio de contexto. Sin embargo, es razonable cuando el tiempo de espera es corto (número de hilos ~ número de núcleos).

fex
fuente