Carreras de datos y el modelo de memoria en Go

Fuentes: Data races and the memory model in Go

En Go, escribir un valor en una goroutine y leerlo desde otra puede parecer correcto cuando el programa imprime el resultado esperado, pero el lenguaje no garantiza que una escritura sea visible para otra goroutine sin sincronización explícita. Este artículo analiza por qué un ejemplo aparentemente funcional —un booleano de señalización combinado con un bucle de espera— activa nonetheless el detector de carreras -race de Go y qué implicaciones tiene según el modelo de memoria del lenguaje.

El texto recorre dos carreras detectadas en ese mismo fragmento. La primera afecta a la variable done: el bucle for !done realiza lecturas no atómicas que el compilador puede reutilizar desde un registro, por lo que la goroutine principal podría leer false de forma indefinida aunque la otra haya escrito true. La segunda afecta a msg: aunque la escritura a msg precede en el código fuente a la de done, el modelo de memoria no garantiza que cuando main abandona el bucle, msg contenga "hello", porque cada variable es una ubicación de memoria independiente y las lecturas carecen de orden entre goroutines.

Se explican también los casos donde el acceso es atómico a nivel hardware (como un bool en arm64, escrito con una sola instrucción MOVB) frente a valores multibyte —structs, slices, cadenas, interfaces— donde Go puede escribir o leer por partes y producir un valor inconsistente que mezcla estados antiguos y nuevos. El artículo termina planteando un segundo ejemplo con dos goroutines que escriben y leen variables cruzadas para ilustrar los resultados no intuitivos que el modelo permite. La conclusión implícita es que el detector de carreras y el modelo de memoria son las referencias autoritativas: cualquier coordinación entre goroutines debe hacerse con primitivas de sincronización del propio lenguaje.