thread lock example clase c++ multithreading mutex

lock - thread c++ definition



¿Cómo un mutex asegura que el valor de una variable sea consistente en todos los núcleos? (3)

La consistencia a través de los núcleos está asegurada por las barreras de memoria (lo que también evita que se reordenen las instrucciones). Cuando usa std::atomic , no solo accede a los datos de forma atómica, sino que el compilador (y la biblioteca) también insertan las barreras de memoria relevantes.

Mutexes funciona de la misma manera: las implementaciones de mutex (por ejemplo, pthreads o WinAPI o lo que no) internamente también insertan barreras de memoria.

Si tengo un solo int en el que quiero escribir desde un hilo y leer en otro, necesito usar std::atomic para asegurar que su valor sea coherente en todos los núcleos, independientemente de si las instrucciones que se leen de Y escribirle son conceptualmente atómicos. Si no lo hago, puede ser que el núcleo de lectura tenga un valor antiguo en su caché y no vea el nuevo valor. Esto tiene sentido para mí.

Si tengo algún tipo de datos complejo que no se puede leer / escribir de forma atómica, debo proteger el acceso a él usando alguna primitiva de sincronización, como std::mutex . Esto evitará que el objeto entre (o sea leído) en un estado incoherente. Esto tiene sentido para mí.

Lo que no tiene sentido para mí es cómo la ayuda mutex ayuda con el problema de almacenamiento en caché que resuelve la atómica. Parece que existen únicamente para evitar el acceso simultáneo a algún recurso, pero no para propagar los valores contenidos dentro de ese recurso a los cachés de otros núcleos. ¿Hay alguna parte de su semántica que me he perdido que se ocupa de esto?


La mayoría de los procesadores multinúcleo modernos (incluidos x86 y x64) tienen coherencia de caché . Si dos núcleos tienen la misma ubicación de memoria en el caché y uno de ellos actualiza el valor, el cambio se propaga automáticamente a los cachés de otros núcleos. Es ineficiente (escribir en la misma línea de caché al mismo tiempo desde dos núcleos es realmente lento) pero sin la coherencia de caché sería muy difícil escribir software de multiproceso.

Y como dijo Syam, también se requieren barreras de memoria. Evitan que el compilador o el procesador vuelvan a ordenar los accesos a la memoria y también forzan la escritura en la memoria (o al menos en la memoria caché), cuando, por ejemplo, una variable se mantiene en un registro debido a las optizaciones del compilador.


La respuesta correcta a esto es magia pixies - por ejemplo, simplemente funciona. La implementación de std :: atomic para cada plataforma debe hacer lo correcto.

Lo correcto es una combinación de 3 partes.

En primer lugar, el compilador necesita saber que no puede mover las instrucciones más allá de los límites [de hecho, en algunos casos puede hacerlo, pero suponga que no lo hace].

En segundo lugar, el subsistema de memoria / caché debe saberlo: esto generalmente se hace usando barreras de memoria, aunque x86 / x64 generalmente tienen una memoria tan fuerte que garantiza que esto no es necesario en la gran mayoría de los casos (lo cual es una vergüenza). para que el código incorrecto realmente salga mal).

Finalmente, la CPU necesita saber que no puede reordenar las instrucciones. Las CPU modernas son muy agresivas al reordenar las operaciones y asegurarse de que esto sea imperceptible en el caso de un solo hilo . Es posible que necesiten más pistas de que esto no puede suceder en ciertos lugares.

Para la mayoría de las CPU, las partes 2 y 3 se reducen a lo mismo: una barrera de memoria implica ambas cosas. La Parte 1 está totalmente dentro del compilador, y depende de los escritores del compilador para hacerlo bien.

Vea a Herb Sutters hablar ''Armas atómicas'' para obtener información mucho más interesante.