C++Modelo de memoria C ++ 11


Observaciones

Los diferentes subprocesos que intentan acceder a la misma ubicación de memoria participan en una carrera de datos si al menos una de las operaciones es una modificación (también conocida como operación de almacenamiento ). Estas razas de datos causan un comportamiento indefinido . Para evitarlos, es necesario evitar que estos subprocesos ejecuten simultáneamente dichas operaciones en conflicto.

Las primitivas de sincronización (exclusión mutua, sección crítica y similares) pueden proteger tales accesos. El modelo de memoria introducido en C ++ 11 define dos nuevas formas portátiles de sincronizar el acceso a la memoria en un entorno de múltiples subprocesos: operaciones atómicas y cercas.

Operaciones atómicas

Ahora es posible leer y escribir en una ubicación de memoria determinada mediante el uso de carga atómica y operaciones de almacenamiento atómico . Para mayor comodidad, estos se incluyen en la clase de plantilla std::atomic<t> . Esta clase ajusta un valor de tipo t pero esta vez se carga y se almacena en el objeto atómico.

La plantilla no está disponible para todos los tipos. Los tipos disponibles son específicos de la implementación, pero esto generalmente incluye la mayoría (o todos) los tipos integrales disponibles, así como los tipos de punteros. Así que std::atomic<unsigned> y std::atomic<std::vector<foo> *> deberían estar disponibles, mientras que std::atomic<std::pair<bool,char>> probablemente no estará disponible.

Las operaciones atómicas tienen las siguientes propiedades:

  • Todas las operaciones atómicas se pueden realizar simultáneamente desde varios subprocesos sin causar un comportamiento indefinido.
  • Una carga atómica verá el valor inicial con el que se construyó el objeto atómico o el valor que se le escribió a través de alguna operación de almacenamiento atómico .
  • Las tiendas atómicas para el mismo objeto atómico se ordenan de la misma manera en todos los hilos. Si una hebra ya ha visto el valor de alguna operación de almacenamiento atómico , las operaciones de carga atómica subsiguientes verán el mismo valor o el valor almacenado por la operación de almacenamiento atómico subsiguiente.
  • Las operaciones atómicas de lectura-modificación-escritura permiten que la carga atómica y el almacenamiento atómico ocurran sin otro almacenamiento atómico en el medio. Por ejemplo, uno puede incrementar atómicamente un contador desde varios subprocesos, y no se perderá ningún incremento, independientemente de la contención entre los subprocesos.
  • Las operaciones atómicas reciben un parámetro opcional std::memory_order que define qué propiedades adicionales tiene la operación con respecto a otras ubicaciones de memoria.
std :: memory_order Sentido
std::memory_order_relaxed sin restricciones adicionales
std::memory_order_releasestd::memory_order_acquire si load-acquire ve el valor almacenado por store-release entonces los almacenes secuenciados antes store-release ocurra el store-release antes de las cargas secuenciadas después de la adquisición de la carga
std::memory_order_consume como memory_order_acquire pero solo para cargas dependientes
std::memory_order_acq_rel Combina load-acquire y store-release
std::memory_order_seq_cst consistencia secuencial

Estas etiquetas de orden de memoria permiten tres disciplinas de ordenación de memoria diferentes: coherencia secuencial , relajada y adquisición de versión con su versión de consumo de hermanos.

Consistencia secuencial

Si no se especifica ningún orden de memoria para una operación atómica, el orden se establece de manera predeterminada en consistencia secuencial . Este modo también se puede seleccionar explícitamente etiquetando la operación con std::memory_order_seq_cst .

Con este orden, ninguna operación de memoria puede cruzar la operación atómica. Todas las operaciones de memoria secuenciadas antes de la operación atómica suceden antes de la operación atómica y la operación atómica ocurre antes de todas las operaciones de memoria que se secuencian después de esta. Este modo es probablemente el más fácil de razonar, pero también conduce a la mayor penalización para el rendimiento. También evita todas las optimizaciones del compilador que de otro modo podrían intentar reordenar las operaciones después de la operación atómica.

Pedidos relajados

Lo opuesto a la consistencia secuencial es el ordenamiento de memoria relajado . Se selecciona con la etiqueta std::memory_order_relaxed . La operación atómica relajada no impondrá restricciones en otras operaciones de memoria. El único efecto que queda, es que la operación sigue siendo atómica.

Liberar-Adquirir pedidos

Una operación de almacenamiento atómico se puede etiquetar con std::memory_order_release y una operación de carga atómica se puede etiquetar con std::memory_order_acquire . La primera operación se llama liberación atómica (store-release), mientras que la segunda se llama adquisición (carga atómica) .

Cuando la adquisición de carga ve el valor escrito por un lanzamiento de tienda, sucede lo siguiente: todas las operaciones de almacenamiento secuenciadas antes de la liberación de almacenamiento se vuelven visibles ( suceden antes ) las operaciones de carga que se secuencian después de la adquisición de carga .

Las operaciones atómicas de lectura-modificación-escritura también pueden recibir la etiqueta acumulativa std::memory_order_acq_rel . Esto hace que la porción de carga atómica de la operación sea una carga atómica adquirida, mientras que la porción de almacenamiento atómico se convierte en liberación de almacenamiento atómico .

Al compilador no se le permite mover las operaciones de la tienda después de una operación de liberación atómica de la tienda . Tampoco está permitido mover las operaciones de carga antes de la carga atómica de adquisición (o carga de consumo ).

También tenga en cuenta que no hay liberación de carga atómica o adquisición de almacén atómica . Intentar crear tales operaciones hace que sean operaciones relajadas .

Orden de liberación de consumo

Esta combinación es similar a la adquisición por versión , pero esta vez la carga atómica se etiqueta con std::memory_order_consume y se convierte en una std::memory_order_consume (atómica) de consumo de carga . Este modo es el mismo que el de adquisición de versión, con la única diferencia de que entre las operaciones de carga secuenciadas después de la carga y el consumo, solo en función del valor cargado por la carga y el consumo se ordenan.

Vallas

Las cercas también permiten que las operaciones de memoria se ordenen entre hilos. Una valla es una valla de liberación o adquirir una valla.

Si una valla de liberación ocurre antes de una cerca de adquisición, entonces las tiendas secuenciadas antes de la cerca de liberación son visibles para las cargas secuenciadas después de la cerca de adquisición. Para garantizar que el cerco de liberación ocurra antes del cercado de adquisición, se pueden usar otras primitivas de sincronización, incluidas las operaciones atómicas relajadas.

Modelo de memoria C ++ 11 Ejemplos relacionados