Modelo de memoria y acceso concurrente
Define la semántica del almacenamiento en memoria de la computadora para el propósito de la máquina abstracta de C++.
La memoria disponible para un programa en C++ es una o más secuencias continuas de bytes. Cada byte en memoria tiene una dirección única.
Contenido |
[editar] Byte
Un byte es la unidad de memoria más pequeña direccionable. Se define como una secuencia contigua de bits, de tamaño suficiente para albergar el valor de cualquier unidad de código UTF-8
(256 valores distintos) (desde C++14) y cualquier miembro del conjunto de caracteres básicos de ejecución (los 96 caracteres que se requiere que sean un solo byte). Al igual que en C, C++ soporta bytes de tamaño de 8 bits y mayores.
Los tipos char, unsigned char, y signed char usan un solo byte para almacenar y representación del valor. Se puede conocer el número de bits de un byte mediante CHAR_BIT o std::numeric_limits<unsigned char>::digits.
[editar] Ubicación de memoria
Una ubicación de memoria es:
- un objeto de tipo escalar (tipo aritmético, tipo de puntero, tipo de enumeración o std::nullptr_t);
- o la mayor secuencia contigua de campos de bits que no tenga tamaño cero.
Nota: varias características del lenguaje, tales como referencias y funciones virtuales, pueden incluir ubicaciones adicionales de memoria que no son accesibles al programa pero que son gestionadas por la implementación.
struct S { char a; // ubicación de memoria #1 int b : 5; // ubicación de memoria #2 int c : 11, // ubicación de memoria #2 (continúa) : 0, d : 8; // ubicación de memoria #3 struct { int ee : 8; // ubicación de memoria #4 } e; } obj; // El objeto 'obj' consiste en 4 ubicaciones de memoria separadas
[editar] Hilos y carreras de datos
Un hilo de ejecución es un flujo de control en un programa que comienza con la invocación de una función de alto nivel por std::thread::thread, std::async, u otros medios.
Cualquier hilo puede potencialmente acceder a cualquier objeto en el programa (los objetos con duración de almacenamiento automática y local al hilo aún pueden ser accedidos por otro hilo a través de un puntero o por referencia).
Siempre se permite que diferentes hilos de ejecución accedan (lean y modifiquen) a diferentes ubicaciones de memoria concurrentemente, sin interferencia y sin requisitos de sincronización.
Cuando la evaluación de una expresión escribe en una ubicación de memoria y otra evaluación lee o modifica la misma ubicación, se dice que las expresiones entran en conflicto. Un programa que tiene dos evaluaciones en conflicto tiene una carrera de datos a menos que
- ambas evaluaciones se ejecuten en el mismo hilo o el mismo controlador de señales, o
- ambas evaluaciones en conflicto sean operaciones atómicas (véase std::atomic), o
- una de las evaluaciones sucede-antes que la otra (véase std::memory_order).
Si ocurre un problema de concurrencia, el comportamiento del programa no está definido.
(En particular, la implementación del cierre de exlusión mutua (std::mutex) se sincroniza-con, y por lo tanto, sucede-antes de la toma del mismo mutex por otro hilo, lo que hace posible el uso de cierres de exclusión mutua para protegerse de carreras de datos).
int cnt = 0; auto f = [&]{cnt++;}; std::thread t1{f}, t2{f}, t3{f}; // comportamiento no definido
std::atomic<int> cnt{0}; auto f = [&]{cnt++;}; std::thread t1{f}, t2{f}, t3{f}; // de acuerdo
[editar] Orden de memoria
Cuando un hilo lee un valor en una ubicación de memoria, puede ver el valor inicial, el valor escrito en el mismo hilo, o el valor escrito por otro hilo. Veáse std::memory_order para obtener detalles sobre el orden en que las escrituras hechas por hilos se hacen visibles a otros hilos.
[editar] Progreso de avance
[editar] Libre de obstrucción
Cuando sólo un hilo que no está bloqueado en una función de la biblioteca estándar ejecuta una función atómica que esta libre de bloqueos, se garantiza que la ejecución se completa (Todas las operaciones sin bloqueos de la biblioteca estándar están libres de obstrucción).
[editar] Libre de bloqueo
Cuando se ejecutan concurrentemente una o más funciones atómicas libres de bloqueo, se garantiza que al menos una de ellas se completa (todas las operaciones libres de bloqueo de la biblioteca estándar son libres de bloqueo – es trabajo de la implementación asegurar que no se puedan bloquear de forma indefinida por otros hilos, como hurtar continuamente la línea de caché).
[editar] Garantía de progreso
En un programa C++ válido, cada hilo eventualmente realiza una de las acciones siguientes:
- finaliza;
- realiza una llamada a función de la biblioteca de E/S;
- realiza un acceso mediante un gl-valor volátil;
- realiza una operación atómica o de sincronización.
Ningún hilo de ejecución puede ejecutarse para siempre sin realizar alguno de estos comportamientos observables.
Ten en cuenta que esto significa que un programa con recursividad infinita o bucle infinito (implementado como una instrucción for o por un goto en bucle, o de otra manera) tiene un comportamiento no definido. Esto permite a los compiladores eliminar todos los bucles que no tienen un comportamiento observable, sin tener que comprobar que eventualmente pueden terminar.
Se dice que un hilo progresa si realiza uno de los pasos de ejecución anteriores (E/S, volátil, atómico, o sincronización), está bloqueado en una función de la biblioteca estándar, o llama a una función atómica libre de bloqueo que no se completa debido a un hilo concurrente no bloqueado.
Progreso de avance concurrenteSi un hilo ofrece garantía de progreso de avance concurrente, progresará (como se definió anteriormente) en un tiempo finito, mientras no haya terminado, independientemente de si hay otros hilos (si es que los hay) en progreso. El estándar aconseja, pero no exige que el hilo principal y los hilos iniciados por std::thread garanticen progreso de avance concurrente. Progreso de avance paraleloSi un hilo ofrece garantía de progreso paralelo, no se requiere que la implementación asegure que el hilo progresará si aún no ha ejecutado ningún paso de ejecución (E/S, volátil, atómica, o sincronización), pero una vez que este hilo ha ejecutado un paso, proporciona garantías de progreso de avance concurrente (esta regla describe un hilo en un grupo de hilos que ejecutan tareas en orden arbitrario). Progreso de avance débilmente paraleloSi un hilo ofrece garantía de progreso débilmente paralelo, no se garantiza el progreso, independientemente de que los otros hilos progresen o no. Todavía se puede garantizar que esos hilos progresen mediante bloqueo con delegación de garantía de progreso: si un hilo P bloquea de esta forma en la finalización de un conjunto de hilos S, entonces al menos un hilo de S ofrecerá garantía de progreso que será igual o más rígida que P. Una vez que el hilo se completa, otro hilo en S se fortalecerá de manera similar. Una vez que el conjunto está vacío, P se desbloqueará. Los algoritmos paralelos de la biblioteca estándar de C++ bloquean con delegación de progreso en la finalización de un conjunto no especificado de hilos gestionados por la biblioteca. |
(desde C++17) |
[editar] Véase también
Documentación de C para Modelo de memoria
|