¿Debo ocuparme de las condiciones de carrera que casi con certeza no tienen posibilidades de ocurrir?

52

Consideremos algo así como una aplicación GUI en la que el subproceso principal está actualizando la IU casi instantáneamente, y algún otro subproceso está sondeando datos a través de la red o algo que se garantiza que tomará entre 5 y 10 segundos para finalizar el trabajo.

He recibido muchas respuestas diferentes para esto, pero algunas personas dicen que si es una condición de carrera de una imposibilidad estadística, no se preocupen en absoluto, pero otros han dicho que si hay incluso un 10 -53 % (bromeo no en los números, esto es lo que he escuchado) de que ocurre algo de magia vudú debido a la condición de la carrera, siempre obtenga / suelte bloqueos en el hilo que lo necesita.

¿Cuáles son tus pensamientos? ¿Es una buena práctica de programación manejar la condición de carrera en situaciones estadísticamente imposibles? ¿O sería totalmente innecesario o incluso contraproducente agregar más líneas de código para dificultar la legibilidad?

l46kok
fuente
21
Cuando la gente declara oportunidades como esa, ¿por qué nadie pregunta sobre la educación de la persona que indica ese número? Necesita una educación formal en estadística antes de poder respaldar con un número como ese.
Pieter B
27
Como físico, p <1E-140 significa p = 0. No va a suceder en este universo. 0.00000000000000000000000000000000000000000000000000001% es mucho más grande.
MSalters
15
Asegúrate de que esta condición de carrera no pueda provocar que alguien bloquee tu aplicación voluntariamente . Esta podría ser la causa de un problema de seguridad.
toasted_flakes
27
Uno de cada millones de posibilidades ocurre nueve de cada diez veces.
Kaz Dragon
27
"casi con certeza no tiene posibilidad de ocurrir?" significa que ocurre en la producción a las 3 AM y probablemente sea muy costoso.

Respuestas:

137

Si es realmente un evento 1 en 10 ^ 55, no habría necesidad de codificarlo. Eso implicaría que si realizaras la operación 1 millón de veces por segundo, obtendrías un error cada 3 * 10 ^ 41 años, que es, aproximadamente, 10 ^ 31 veces la edad del universo. Si su aplicación tiene un error solo una vez en cada billón de billones de billones de edades del universo, probablemente sea lo suficientemente confiable.

Sin embargo, apostaría mucho que el error no es tan poco probable. Si puede concebir el error, es casi seguro que ocurrirá al menos ocasionalmente, lo que hace que valga la pena codificarlo correctamente. Además, si codifica los subprocesos correctamente desde el principio para que obtengan y liberen bloqueos de manera adecuada, el código será mucho más fácil de mantener en el futuro. No tiene que preocuparse cuando realiza un cambio, debe volver a analizar todas las condiciones de carrera potenciales, volver a calcular sus probabilidades y asegurarse de que no se repitan.

Justin Cave
fuente
66
Recuerdo un comentario que leí hace años pero que no puedo encontrar ahora "Una probabilidad de 1 en un millón suele ser el próximo martes". +1 por decir que es "de ninguna manera tan improbable".
Bevan
2
+1 para la apuesta. La mejor manera de lidiar con las condiciones de carrera es deshacerse de ellas.
Blrfl
10
@Bevan "Una probabilidad de 1 en un millón suele ser el próximo martes" ... a menos que esté jugando una lotería :)
dasblinkenlight
22
@dasblinkenlight Pero las posibilidades de que alguien gane en la mayoría de las loterías se acercan al 100%. Predecir quién , ahora ese es el desafío.
Bevan
3
@Bevan: Ese comentario fue exactamente lo que estaba pasando por mi mente cuando leí la pregunta: aquí está la referencia: blogs.msdn.com/b/larryosterman/archive/2004/03/30/104165.aspx
Doc Brown
69

Desde el punto de vista de costo-beneficio, debe escribir código adicional solo cuando obtenga suficiente beneficio.

Por ejemplo, si lo peor que sucedería si un hilo incorrecto "gana la carrera" es que la información no se mostrará, y el usuario tendría que hacer clic en "actualizar", no se moleste en protegerse contra la condición de la carrera: tener que escribir mucho código no vale la pena arreglar algo tan insignificante.

Por otro lado, si la condición de carrera podría resultar en transferencias de dinero incorrectas entre cuentas bancarias, entonces debe protegerse contra la condición de carrera, sin importar cuánto código necesite escribir para resolver este problema.

dasblinkenlight
fuente
20
+1: Por hacer la distinción entre "Fracaso que parece fracaso" y "Fracaso que parece éxito". La información incorrecta es mucho más grave, según el dominio.
deworde
2
+1 marca una gran diferencia cuáles podrían ser los resultados de la condición de carrera.
Grant
+1 La consecuencia de la condición de carrera debe ser un factor decisivo importante en si debe abordarse. Una condición de carrera que podría causar un accidente aéreo es muy diferente de una condición que podría obligar al usuario a volver a abrir una aplicación.
Poke
1
+1: Diría que las consecuencias son probablemente lo que deberías analizar y no la probabilidad de que ocurra. Si las consecuencias no importan, es posible que no tenga que manejar la condición de carrera INCLUSO si es muy común.
Leo
1
Pero no asuma que arreglar una condición de carrera automáticamente significa que tiene que escribir más código. También podría significar eliminar una gran porción de código con errores y reemplazarla con una porción más pequeña de código correcto.
JesperE
45

Encontrar una condición de carrera es la parte difícil. Probablemente haya pasado casi tanto tiempo escribiendo esta pregunta como le habría tomado resolverla. No es que lo haga mucho menos legible. Los programadores esperan ver el código de sincronización en tales situaciones, y en realidad podrían perder más tiempo preguntándose por qué no está allí y si agregarlo solucionaría su error no relacionado.

En cuanto a las probabilidades, se sorprendería. Tuve un informe de error de condición de carrera el año pasado que no pude reproducir con miles de intentos automatizados, pero un sistema de un cliente lo vio todo el tiempo. El valor comercial de pasar 5 minutos para solucionarlo ahora, en lugar de posiblemente solucionar un error "imposible" en la instalación de un cliente, hace que la elección sea obvia.

Karl Bielefeldt
fuente
1
¡Esto también! Evite que otros programadores reflexionen sobre posibles problemas al leer su código, haciendo lo que sea necesario (incluso si es 'improbable' que falle).
Casey Kuball
Su punto está bien tomado (las correcciones realizadas ahora son más rápidas y más baratas que las realizadas más adelante), excepto que nunca será solo "5 minutos para solucionarlo ahora".
iconoclasta
2
1 por señalar que la probabilidad de la condición de carrera, probablemente depende de muchos factores, por lo que incluso si parece poco probable en su configuración, puede ocurrir con más frecuencia en un sistema cliente / en un sistema operativo diferente / en la próxima versión, etc.
sleske
27

Obtenga y suelte las cerraduras. Las probabilidades cambian, los algoritmos cambian. Es un mal hábito entrar, y cuando algo sale mal no tienes que detenerte y preguntarte si te equivocaste ...

jmoreno
fuente
66
+1 para cambio de algoritmos. En este momento, cuando conoce la condición de la carrera, las probabilidades son bajas. Después de un año, cuando se haya olvidado de la condición de la carrera, puede hacer un cambio en su código que cambia significativamente el tiempo y la probabilidad de un error.
Phil
13

y algún otro hilo sondeando datos a través de la red o algo que se garantiza que demorará entre 5 y 10 segundos en finalizar el trabajo.

Hasta que alguien introduce una capa de almacenamiento en caché para mejorar el rendimiento. De repente, esa otra banda de rodadura terminó casi instantáneamente y la condición de la carrera se manifiesta con mayor frecuencia que no.

Si esto sucediera hace unas semanas, tardé unos 2 días completos en encontrar el error.

Siempre arregle las condiciones de carrera si las reconoce.

Michael Borgwardt
fuente
8

Simple vs correcto.

En muchos casos, la simplicidad triunfa sobre la corrección. Es un problema de costos.

Además, las condiciones de carrera son cosas desagradables que tienden a no obedecer a estadísticas simples. Todo va bien hasta que otra sincronización aparentemente no relacionada hace que su condición de carrera ocurra repentinamente la mitad del tiempo. A menos que encienda los registros o depure el código, por supuesto.

Una alternativa pragmática para prevenir una condición de carrera (que puede ser difícil) puede ser detectarla y registrarla (bonificación por fallar duro y temprano). Si nunca sucede, perdiste poco. Si realmente sucede, tienes una justificación sólida para dedicar el tiempo extra a arreglarlo.

ptyx
fuente
1
+1 para iniciar sesión y fallar temprano si arreglarlo directamente es demasiado complicado.
Martin Ba
En muchos casos, la simplicidad triunfa sobre la integridad. La sincronización casi nunca se encuentra entre esos casos. Casi siempre volverá a morderte (o al pobre encargado de mantener tu código) más tarde.
reirab
@reirab No estoy de acuerdo. Si considera eventos poco frecuentes, la falla registrada es rentable. Un ejemplo: si la aplicación de su teléfono tiene una tasa de falla de 1/100 (bloqueo) si el usuario está cambiando la red en un mes exacto de transición (1/31 23:59:00 -> 2/1 00:00:00), usted Probablemente nunca escuche sobre eso. Pero entonces una probabilidad de 1/10 ^ 9 de bloqueo en la conexión en un servidor es inaceptable. Depende.
ptyx
7

Si su condición de carrera está relacionada con la seguridad, siempre debe codificar para evitarla.

Un ejemplo común son las condiciones de carrera con la creación / apertura de archivos en Unix, que en algunas circunstancias pueden conducir a ataques de escalada de privilegios si el programa con la condición de carrera se ejecuta con mayores privilegios que el usuario que interactúa con él, como un proceso de sistema demonio o peor aún, el núcleo.

Incluso si una condición de carrera tiene una probabilidad de 10 ^ (- 80) de ocurrir aleatoriamente , es muy posible que un atacante determinado tenga una posibilidad decente de crear tales condiciones de manera deliberada y artificial.

Bristol
fuente
6

Therac-25!

Los desarrolladores del proyecto Therac-25 tenían bastante confianza en el momento entre una interfaz de usuario y un problema relacionado con la interfaz en una máquina de rayos X terapéutica.

No deberían haber sido.

Puede obtener más información sobre este famoso desastre de software de vida o muerte en:

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

o

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

Su aplicación puede ser mucho menos sensible a fallas que los dispositivos médicos. Un método útil es calificar la exposición al riesgo como el producto de la probabilidad de ocurrencia y el costo de ocurrencia durante la vida del producto para todas las unidades que podrían producirse.

Si ha elegido construir su código para que dure (y parece que lo ha hecho), debe considerar la ley de Moore que puede eliminar fácilmente varios ceros cada pocos años a medida que las computadoras dentro o fuera de su sistema se vuelven más rápidas. Si envía miles de copias, corte más ceros. Si los usuarios realizan esta operación diariamente (o mensualmente) durante años, elimine algunos más. Si se usa donde está disponible la fibra de Google, ¿entonces qué? Si la basura de la interfaz de usuario se acumula a mediados de la operación de la GUI, ¿eso afecta a la carrera? ¿Está utilizando una biblioteca de código abierto o de Windows detrás de su GUI? ¿Pueden las actualizaciones afectar el tiempo?

Los semáforos, bloqueos, mutexes, sincronización de barreras se encuentran entre las formas de sincronizar actividades entre subprocesos. Potencialmente, si no los está utilizando, otra persona que mantiene su programa podría y luego, rápidamente, las suposiciones sobre las relaciones entre los hilos pueden cambiar y el cálculo sobre la condición de la carrera podría invalidarse.

Le recomiendo que sincronice explícitamente porque si bien es posible que nunca lo vea crear un problema, un cliente podría hacerlo. Además, incluso si su condición de carrera nunca ocurre, ¿qué sucede si usted o su organización son llamados a la corte para defender su código (como Toyota estuvo relacionado con el Prius hace unos años)? Cuanto más exhaustiva sea tu metodología, mejor te irá. Puede ser mejor decir "nos protegemos de este caso improbable como este ..." que decir "sabemos que nuestro código fallará, pero escribimos esta ecuación para mostrar que no sucederá en nuestra vida. Probablemente". "

Parece que el cálculo de probabilidad proviene de otra persona. ¿Conocen su código y los conoce lo suficiente como para confiar en que no se cometió ningún error? Si calculé una confiabilidad del 99.99997% para algo, también podría pensar en mis clases de estadísticas de la universidad y recordar que no siempre obtuve el 100%, y retrocedo un buen porcentaje en mis propias estimaciones de confiabilidad personal.

DesarrolladorDon
fuente
1
+1 por mención de Therac-25. Muchas lecciones importantes aquí.
Stuart Marks
Si bien creo que esta es una buena respuesta, podría argumentar que su proyecto de interfaz gráfica de usuario aficionado seguramente no hará que las personas mueran si no elimina una condición de carrera.
marktani
No soy muy discutidor, pero si lo fuera, podría argumentar que cada vez que escribimos código debemos escribirlo correctamente. Si podemos practicar sacar las condiciones de carrera de nuestros proyectos de pasatiempo donde el código es más simple y tal vez somos el único autor, estaremos mucho más preparados cuando abordemos proyectos de trabajo donde el trabajo de varios autores debe integrarse juntos.
DesarrolladorDon
4

¿Sería totalmente innecesario o incluso contraproducente agregar más líneas de código para dificultar la legibilidad?

La simplicidad solo es buena cuando también es correcta. Como este código no es correcto, los futuros programadores lo verán inevitablemente cuando busquen un error relacionado.

De cualquier forma que lo maneje (ya sea registrándolo, documentándolo o agregando los bloqueos, esto depende del costo), ahorrará tiempo a otros programadores al mirar el código.

Casey Kuball
fuente
3

Esto dependería del contexto. Si es un juego casual para iPhone, probablemente no. El sistema de control de vuelo para el próximo vehículo espacial tripulado, probablemente. Todo depende de cuáles sean las consecuencias si el resultado 'malo' ocurre medido contra el costo estimado de arreglarlo.

Rara vez hay una 'talla única' respuesta para este tipo de preguntas porque están no programando preguntas, pero en lugar de economía preguntas.

Gran maestro B
fuente
3
"El sistema de control de vuelo para el próximo vehículo espacial tripulado" DEFINITIVAMENTE .
deworde
probablemente ... definitivamente ... dependería de quién estaba en el cohete :-)
GrandmasterB
3

Sí, espera lo inesperado. He pasado horas (en el código de otras personas ^^) rastreando condiciones que nunca deberían suceder.

Cosas como siempre tienen un else, siempre tienen un valor predeterminado en mayúsculas y minúsculas, inicializan variables (sí, realmente ... los errores suceden a partir de esto), verifican en sus bucles las variables reutilizadas para cada iteración, etc.

Si está preocupado por temas específicos, lea blogs, artículos y libros sobre el tema. El tema actual parece ser información inmutable.

Pablo
fuente
3

Solo arréglalo.

He visto exactamente esto. Un hilo logra hacer una solicitud de red a un servidor que realiza una búsqueda compleja en la base de datos y responde antes de que el otro hilo llegue a la siguiente línea de código. Sucede.

Algún cliente en algún lugar decidirá algún día ejecutar algo que acapare todo el tiempo de CPU para el subproceso "rápido" mientras deja el subproceso lento en ejecución, y lo lamentará :)

JohnB
fuente
1

Si ha reconocido una condición de carrera improbable, ¡al menos documente en el código!

EDITAR: debo agregar que lo solucionaría si es posible, pero al momento de escribir lo anterior, ninguna otra respuesta decía explícitamente que al menos documentara el problema en el código.

Mark Hurd
fuente
1
Sí, y al menos intenta detectarlo y registrarlo si sucede. En mi humilde opinión, está perfectamente bien no evitar todos los errores. Pero al menos hazle saber a alguien que ocurrió y que tu suposición de que no fue errónea.
Steve Bennett
0

Creo que si ya sabes cómo y por qué podría suceder, también podría lidiar con eso. Eso es si no ocupa una gran cantidad de recursos.

Sjaak van der Heide
fuente
0

Todo depende de cuáles sean las consecuencias de una condición de carrera. Creo que las personas que responden su pregunta son correctas para su línea de trabajo. El mío es los motores de configuración del enrutador. Para mí, las condiciones de carrera o bien hacen que los sistemas se detengan, corrompan o no estén configurados a pesar de que dicen que fue exitoso. Siempre uso semáforos por enrutador para no tener que limpiar nada a mano.

Creo que parte de mi código GUI todavía es propenso a las condiciones de carrera de tal manera que un usuario podría recibir un error porque ocurrió una condición de carrera, pero no tendría esas posibilidades si existe la posibilidad de corrupción de datos o mal comportamiento del aplicación después de tal evento.

Sylwester
fuente
0

Curiosamente, me encontré con este problema recientemente. Ni siquiera me di cuenta de que una condición de carrera era posible en mi circunstancia. La condición de carrera solo se presentó cuando los procesadores multi-core se convirtieron en la norma.

El escenario era más o menos así. Un controlador de dispositivo generó eventos para que el software los maneje. El control tuvo que volver al controlador del dispositivo lo antes posible para evitar un tiempo de espera en el dispositivo. Para garantizar esto, el evento se grabó y se puso en cola en un hilo separado.

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.
}

Esto funcionó bien durante años. Entonces, de repente, fallaría en ciertas configuraciones. Resulta que el subproceso de cola ahora se ejecutaba realmente en paralelo al subproceso de gestión de eventos, en lugar de compartir el tiempo de un solo procesador. Logró enviar el siguiente comando al dispositivo antes de que se reconociera el evento, lo que provocó un error fuera de secuencia.

Dado que solo afectó a un cliente en una configuración, puse vergonzosamente Thread.Sleep(1000)dónde estaba el problema. No ha habido un problema desde entonces.

Hand-E-Food
fuente