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?
Respuestas:
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.
fuente
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.
fuente
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.
fuente
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 ...
fuente
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.
fuente
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.
fuente
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.
fuente
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.
fuente
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.
fuente
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.
fuente
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.
fuente
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á :)
fuente
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.
fuente
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.
fuente
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.
fuente
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.
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.fuente