intercambio Atómica en GNU C ++
-
21-09-2019 - |
Pregunta
Quiero comprobar que mi interpretación es correcta. Este tipo de cosas es complicado así que estoy casi seguro de que me falta algo. Tengo un programa que consiste en un hilo en tiempo real y un hilo no en tiempo real. Quiero que el hilo no RT para ser capaz de cambiar un puntero a la memoria que se utiliza por el hilo RT.
A partir de la documentación, mi entendimiento es que esto se puede lograr en g++
con:
// global
Data *rt_data;
Data *swap_data(Data *new_data)
{
#ifdef __GNUC__
// Atomic pointer swap.
Data *old_d = __sync_lock_test_and_set(&rt_data, new_data);
#else
// Non-atomic, cross your fingers.
Data *old_d = rt_data;
rt_data = new_data;
#endif
return old_d;
}
Este es el único lugar en el programa (que no sea la configuración inicial), donde se modifica rt_data
. Cuando rt_data
se utiliza en el contexto en tiempo real, se copia a un puntero local. Para old_d
, más adelante, cuando es seguro que la vieja memoria no se utiliza, se liberó en el hilo no RT. ¿Es esto correcto? ¿Necesito volatile
en cualquier lugar? ¿Hay otras primitivas de sincronización que debería estar llamando?
Por cierto que estoy haciendo esto en C ++, aunque estoy interesado en saber si la respuesta es diferente para C.
Gracias de antemano.
Solución
En general, no use volatile
al escribir código concurrente en C/C++
. La semántica de volatile
están tan cerca de lo que quiere que es tentador, pero al final es volátil no es suficiente . Desafortunadamente Java/C# volatile != C/C++ volatile
. Herb Sutter tiene un artículo rel="noreferrer"> href="http://herbsutter.wordpress.com/2009/01/12/effective-concurrency-volatile-vs-volatile/" gran explicar la confuso desorden.
Lo que realmente quiere es una valla de memoria. __sync_lock_test_and_set
proporciona el vallado para usted.
También necesitará una valla de memoria cuando se copia (carga) el puntero rt_data a su copia local.
Bloqueo de programación libre es complicado. Si usted está dispuesto a utilizar C ++ de GCC extensiones 0x, que es un poco más fácil:
#include <cstdatomic>
std::atomic<Data*> rt_data;
Data* swap_data( Data* new_data )
{
Data* old_data = rt_data.exchange(new_data);
assert( old_data != new_data );
return old_data;
}
void use_data( )
{
Data* local = rt_data.load();
/* ... */
}
Otros consejos
Actualizar : Esta respuesta no es correcta, ya que me falta el hecho de que las garantías volatile
que accede a volatile
variables no son reordenados, pero no proporciona ningún tipo de garantía respecto a otra no volatile
accesos y manipulaciones. Una valla de memoria proporciona estas garantías, y es necesario para esta aplicación. Mi respuesta original está por debajo, pero no actúan sobre ella. Ver esta respuesta para una buena explicación en el agujero en el entendimiento de que llevó a la siguiente respuesta incorrecta.
Respuesta original:
Sí, es necesario volatile
en su declaración rt_data
; volatile
cualquier momento una variable puede ser modificado fuera del flujo de control de un hilo de acceder a él, debe ser declarada . Si bien es posible que pueda salir de allí sin volatile
ya que está copiando a un puntero local volatile
al menos ayuda con la documentación y también inhibe algunas optimizaciones del compilador que pueden causar problemas. Considere el ejemplo siguiente, adoptado de DDJ :
volatile int a;
int b;
a = 1;
b = a;
Si es posible que tenga a
su valor cambió entre a=1
y b=a
, a continuación, a
volatile
debe declararse (a menos que, por supuesto, la asignación de un valor fuera de fecha para b
es aceptable). Multihilo, sobre todo a partir de primitivas atómicas, constituye una situación de este tipo. La situación también se activa con variables modificadas por manejadores de señales y por variables asignadas a posiciones de memoria impares (por ejemplo, hardware de E / S registros). Ver también esta pregunta .
De lo contrario, se ve muy bien a mí.
En C, probablemente usaría las primitivas atómicas proporcionados por GLib para esto. Que vamos a usar una operación atómica cuando estén disponibles y caen de nuevo a una aplicación correcta lenta pero basada en exclusión mutua si las operaciones atómicas no están disponibles. Boost puede proporcionar algo similar para C ++.