Suponga que el hilo 1 está haciendo almacenes atómicos en una variable v usando memory_order_release (o cualquier otro orden) y el hilo 2 está haciendo lecturas atómicas en v usando memory_order_relaxed.

En este caso, debería ser imposible tener lecturas parciales. Un ejemplo de lecturas parciales sería leer la primera mitad de v del último valor y la segunda mitad de v del valor anterior.

  1. Si el hilo 2 ahora lee v sin usar operaciones atómicas, ¿podemos tener lecturas parciales en teoría?
  2. ¿Podemos tener lecturas parciales en la práctica? (Preguntar porque creo que esto no debería importar en la mayoría de los procesadores, pero no estoy seguro).
0
user869887 20 oct. 2019 a las 20:56

1 respuesta

La mejor respuesta

Para 1. ¿Cómo propones hacer eso?

atomic<T> v es una plantilla que sobrecarga la conversión implícita de T() para que sea como .load(mo_seq_cst). Eso hace imposible el desgarro. seq_cst atomic es como relajado más algunas garantías de pedido.

La plantilla también sobrecarga operadores como ++ para hacer un .fetch_add(1, mo_seq_cst) atómico. (O para preincremento, 1 + fetch_add para producir el valor ya incrementado).


Por supuesto, si observa los bytes de la representación de objeto de atomic<T> leyéndolo con char* no atómico (por ejemplo, con memcpy(&tmp, &v, sizeof(int)), eso es UB si otro hilo lo está modificando.

Es más probable que los objetos demasiado grandes no estén bloqueados, pero es posible en algunas implementaciones, p. Ej. para objetos de 8 bytes en un sistema de 32 bits que puede implementar la atomicidad de 8 bytes con instrucciones especiales, pero normalmente solo usará dos cargas de 32 bits.

P.ej. X86 de 32 bits, donde se puede realizar una carga atómica de 8 bytes con SSE y luego devolverla a registros enteros. O lock cmpxchg8b. Los compiladores no hacen eso cuando solo quieren dos registros enteros.

Pero muchos RISC de 32 bits que proporcionan cargas atómicas de 8 bytes tienen una carga de registro doble que produce 2 registros de salida a partir de una instrucción. p.ej. ARM ldrd o MIPS ld. Los compiladores los usan para optimizar cargas alineadas de 8 bytes incluso cuando la atomicidad no es el objetivo, por lo que probablemente "tendrías suerte" y no verías el desgarro de todos modos.

Los objetos pequeños suelen ser atómicos de todos modos; consulte ¿Por qué la asignación de números enteros está ¿Variable alineada atómica en x86?


Por supuesto, el acceso no atómico no supondría que el valor podría cambiar de forma asincrónica, por lo que un bucle podría usar un valor obsoleto de forma indefinida. A diferencia de un atómico relajado, que en los compiladores actuales es como volatile en el sentido de que siempre vuelve a acceder a la memoria. (A través de una caché de hardware coherente, por supuesto, simplemente sin mantener el valor en un registro).

2
Peter Cordes 20 oct. 2019 a las 18:42