* Comportamiento indefinido: Este es el peor de los casos. El resultado del acceso a la memoria es impredecible. La escritura de un procesador puede sobrescribir la otra, o partes de ambos pueden estar entrelazadas, lo que lleva a datos corruptos. No hay garantía de qué operación del procesador tendrá éxito o cómo se verán afectados los datos. Esto es común en sistemas sin ningún mecanismos de sincronización de acceso a la memoria.
* Corrupción de datos: La escritura de un procesador puede sobrescribir los datos escritos por el otro procesador, lo que resulta en datos perdidos o valores incorrectos. Este es un resultado muy común si no hay sincronización.
* Resultado arbitrario: El hardware o el sistema operativo del sistema pueden elegir el acceso de un procesador para tener éxito y el otro para fallar, o puede combinar las operaciones de una manera inesperada. El resultado no es determinista.
* Arbitraje a nivel de hardware: Algunas arquitecturas pueden tener mecanismos de hardware (como un árbitro de bus) que priorizan un procesador sobre el otro. Esto introduce un elemento no determinista, ya que la prioridad puede variar según varios factores.
* Excepción/error: El sistema puede detectar el conflicto y aumentar una excepción o error, potencialmente detener la ejecución o hacer que el programa se bloquee. Sin embargo, esto no está garantizado; Muchos sistemas simplemente permiten que la condición de carrera continúe sin notificación.
Para evitar estos problemas, los programadores deben usar mecanismos de sincronización. Estos mecanismos hacen cumplir el orden de los accesos de memoria, evitando las condiciones de carrera. Los ejemplos incluyen:
* mutexes (exclusión mutua): Solo un procesador puede mantener el Mutex en un momento dado, evitando el acceso simultáneo a los recursos compartidos.
* semáforos: Más general que los mutexes, lo que permite un control más complejo del acceso a los recursos compartidos.
* Operaciones atómicas: Las operaciones garantizadas se ejecutarán atómicamente (como una sola unidad indivisible), evitando la modificación concurrente.
* Barreras de memoria/cercas: Estos hacen cumplir el orden de las operaciones de memoria, asegurando que ciertas operaciones se completen antes de que otras comiencen.
En resumen, el acceso simultáneo a la misma ubicación de memoria sin la sincronización adecuada es un error de programación grave que puede conducir a un comportamiento impredecible y poco confiable. La programación sólida de múltiples procesadores requiere una cuidadosa consideración e implementación de técnicas de sincronización.