Pregunta ¿Qué es una condición de carrera?


Al escribir aplicaciones de subprocesos múltiples, uno de los problemas más comunes experimentados son las condiciones de carrera.

Mis preguntas a la comunidad son:

¿Qué es una condición de carrera? ¿Cómo los detecta? ¿Cómo los manejas? Finalmente, ¿cómo se evita que ocurran?


736
2017-08-29 15:55


origen


Respuestas:


Una condición de carrera ocurre cuando dos o más hilos pueden acceder a los datos compartidos e intentan cambiarlos al mismo tiempo. Debido a que el algoritmo de programación de subprocesos puede intercambiar entre subprocesos en cualquier momento, no se conoce el orden en que los subprocesos intentarán acceder a los datos compartidos. Por lo tanto, el resultado del cambio en los datos depende del algoritmo de programación de hilos, es decir, ambos hilos están "corriendo" para acceder / cambiar los datos.

Los problemas a menudo ocurren cuando un hilo hace un "verificar y actuar" (por ejemplo, "verificar" si el valor es X, luego "actuar" para hacer algo que depende de que el valor sea X) y otro hilo hace algo con el valor de entre el "cheque" y el "acto". P.ej:

if (x == 5) // The "Check"
{
   y = x * 2; // The "Act"

   // If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
   // y will not be equal to 10.
}

El punto es que y podría ser 10, o podría ser cualquier cosa, dependiendo de si otro hilo cambió x entre el cheque y el acto. No tienes forma real de saber.

Para evitar que se produzcan condiciones de carrera, normalmente pondría un candado alrededor de los datos compartidos para garantizar que solo un hilo pueda acceder a los datos a la vez. Esto significaría algo como esto:

// Obtain lock for x
if (x == 5)
{
   y = x * 2; // Now, nothing can change x until the lock is released. 
              // Therefore y = 10
}
// release lock for x

917
2017-08-29 16:05



Una "condición de carrera" existe cuando el código multiproceso (o paralelo) que accedería a un recurso compartido podría hacerlo de tal manera que cause resultados inesperados.

Toma este ejemplo:

for ( int i = 0; i < 10000000; i++ )
{
   x = x + 1; 
}

Si tenía 5 hilos ejecutando este código a la vez, el valor de x NO terminaría en 50,000,000. De hecho, variaría con cada carrera.

Esto se debe a que, para que cada subproceso incremente el valor de x, tienen que hacer lo siguiente: (simplificado, obviamente)

Recuperar el valor de x
Agrega 1 a este valor
Almacene este valor en x

Cualquier hilo puede estar en cualquier paso de este proceso en cualquier momento, y pueden pisar uno al otro cuando se trata de un recurso compartido. El estado de x puede cambiarse por otro hilo durante el tiempo entre x se lee y cuando se escribe de nuevo.

Digamos que un hilo recupera el valor de x, pero aún no lo ha almacenado. Otro hilo también puede recuperar el mismo valor de x (porque ningún subproceso lo ha cambiado aún) y luego ambos estarían almacenando el mismo valor (x + 1) de nuevo en x!

Ejemplo:

Subproceso 1: lee x, el valor es 7
Subproceso 1: agregue 1 a x, el valor ahora es 8
Tema 2: lee x, el valor es 7
Subproceso 1: almacena 8 en x
Subproceso 2: agrega 1 a x, el valor ahora es 8
Tema 2: tiendas 8 en x

Las condiciones de carrera pueden evitarse mediante el empleo de algún tipo de cierre mecanismo antes del código que accede al recurso compartido:

for ( int i = 0; i < 10000000; i++ )
{
   //lock x
   x = x + 1; 
   //unlock x
}

Aquí, la respuesta sale como 50,000,000 cada vez.

Para obtener más información sobre el bloqueo, busque: mutex, semáforo, sección crítica, recurso compartido.


176
2017-08-29 17:01



¿Qué es una condición de carrera?

Estás planeando ir al cine a las 5 p.m. Usted pregunta por la disponibilidad de los boletos a las 4 p.m. El representante dice que están disponibles. Te relajas y alcanzas la taquilla 5 minutos antes del espectáculo. Estoy seguro de que puedes adivinar lo que sucede: es una casa llena. El problema aquí estaba en la duración entre el cheque y la acción. Inquirió a las 4 y actuó a las 5. Mientras tanto, alguien más agarró las entradas. Esa es una condición de carrera, específicamente un escenario de "verificar y actuar" de condiciones de carrera.

¿Cómo los detecta?

Revisión de código religioso, pruebas unitarias de subprocesos múltiples. No hay ningún atajo. Hay pocos plugins de Eclipse emergentes en esto, pero nada estable todavía.

¿Cómo manejas y previenes?

Lo mejor sería crear funciones sin efectos secundarios y sin estado, usar elementos inmutables tanto como sea posible. Pero eso no siempre es posible. Entonces, usar java.util.concurrent.atomic, estructuras de datos concurrentes, sincronización adecuada y concurrencia basada en actores ayudará.

El mejor recurso para la concurrencia es JCIP. También puedes obtener más detalles sobre la explicación anterior aquí.


111
2017-10-04 21:20



Hay una diferencia técnica importante entre las condiciones de carrera y las carreras de datos. La mayoría de las respuestas parecen suponer que estos términos son equivalentes, pero no lo son.

Se produce una carrera de datos cuando 2 instrucciones acceden a la misma ubicación de memoria, al menos uno de estos accesos es una escritura y no hay sucede antes de ordenar entre estos accesos. Ahora, lo que constituye un suceso antes del pedido está sujeto a mucho debate, pero en general los pares ulock-lock en la misma variable de bloqueo y los pares de señal de espera en la misma variable de condición inducen un orden de suceder antes de.

Una condición de carrera es un error semántico. Es un defecto que ocurre en el tiempo o en el orden de los eventos que lleva a un programa erróneo comportamiento.

Muchas condiciones de carrera pueden ser (y de hecho son) causadas por carreras de datos, pero esto no es necesario. De hecho, las carreras de datos y las condiciones de carrera no son ni necesarias ni suficientes para el otro. Esta La publicación de blog también explica la diferencia muy bien, con un ejemplo simple de transacción bancaria. Aquí hay otro simple ejemplo eso explica la diferencia.

Ahora que definimos la terminología, intentemos responder la pregunta original.

Dado que las condiciones de carrera son errores semánticos, no existe una forma general de detectarlos. Esto se debe a que no hay forma de tener un oráculo automático que pueda distinguir entre el comportamiento correcto y el incorrecto del programa en el caso general. La detección de carreras es un problema indecidible.

Por otro lado, las carreras de datos tienen una definición precisa que no se relaciona necesariamente con la corrección y, por lo tanto, se pueden detectar. Hay muchos sabores de detectores de raza de datos (detección de carrera de datos estáticos / dinámicos, detección de carrera de datos basada en cerrojo, detección de carrera de datos basada en lockset anterior, detección de raza híbrida de datos). Un detector de carreras de datos dinámicos de vanguardia es ThreadSanitizer que funciona muy bien en la práctica.

El manejo de las carreras de datos en general requiere cierta disciplina de programación para inducir a que ocurra, antes de los límites entre los accesos a los datos compartidos (durante el desarrollo o una vez que se detectan usando las herramientas mencionadas anteriormente). esto se puede hacer a través de bloqueos, variables de condición, semáforos, etc. Sin embargo, también se pueden emplear diferentes paradigmas de programación, como pasar mensajes (en lugar de memoria compartida), que evitan carreras de datos por construcción.


52
2017-08-29 08:45



Una definición de tipo de canónico es "cuando dos hilos acceden a la misma ubicación en la memoria al mismo tiempo, y al menos uno de los accesos es una escritura. "En la situación, el hilo" lector "puede obtener el valor anterior o el nuevo valor, dependiendo de qué hilo" gana la carrera ". Esto no siempre es un error; de hecho, algunos algoritmos de bajo nivel realmente peludos hacen esto en propósito, pero en general se debe evitar. @Steve Gury da un buen ejemplo de cuándo podría ser un problema.


32
2017-08-29 16:21



Una condición de carrera es un tipo de error, que ocurre solo con ciertas condiciones temporales.

Ejemplo: Imagina que tienes dos hilos, A y B.

En el hilo A:

if( object.a != 0 )
    object.avg = total / object.a

En el hilo B:

object.a = 0

Si se adelanta el hilo A justo después de haber verificado que el objeto. A no es nulo, B lo hará a = 0, y cuando el hilo A obtenga el procesador, hará una "división por cero".

Este error solo ocurre cuando el hilo A se adelanta justo después de la instrucción if, es muy raro, pero puede suceder.


27
2017-08-29 16:03



Las condiciones de carrera ocurren en aplicaciones de subprocesos múltiples o sistemas multiproceso. Una condición de carrera, en su aspecto más básico, es cualquier cosa que suponga que dos cosas que no están en el mismo proceso o subproceso ocurrirán en un orden particular, sin tomar medidas para garantizar que lo hagan. Esto sucede comúnmente cuando dos hilos pasan mensajes estableciendo y verificando las variables miembro de una clase a la que ambos pueden acceder. Casi siempre hay una condición de carrera cuando un hilo se pone en suspensión para dar otro tiempo de hilo para finalizar una tarea (a menos que el sueño esté en un bucle, con algún mecanismo de comprobación).

Las herramientas para prevenir las condiciones de carrera dependen del lenguaje y del sistema operativo, pero algunos comon son mutex, secciones críticas y señales. Los mutexes son buenos cuando quieres asegurarte de que eres el único que está haciendo algo. Las señales son buenas cuando quieres asegurarte de que alguien más haya terminado de hacer algo. Minimizar los recursos compartidos también puede ayudar a prevenir comportamientos inesperados

Detectar las condiciones de carrera puede ser difícil, pero hay un par de signos. El código que depende en gran medida de las horas de sueño es propenso a las condiciones de carrera, por lo que primero verifique que las llamadas duerman en el código afectado. También se puede utilizar la adición de long sleeps para la depuración para intentar forzar un orden particular de eventos. Esto puede ser útil para reproducir el comportamiento, ver si puede hacerlo desaparecer cambiando el momento de las cosas y para poner a prueba las soluciones de prueba. Los sleeps deben eliminarse después de la depuración.

Sin embargo, el signo distintivo de que uno tiene una condición de carrera es si hay un problema que solo ocurre intermitentemente en algunas máquinas. Los errores comunes serían bloqueos y bloqueos. Con el registro, debería poder encontrar el área afectada y trabajar desde allí.


18
2017-08-29 16:12