Domanda

Voglio verificare che la mia comprensione è corretta. Questo genere di cose è difficile quindi sono quasi sicuro che mi manca qualcosa. Ho un programma costituito da un filo in tempo reale ed un filo non in tempo reale. Voglio che il filo non-RT per essere in grado di scambiare un puntatore alla memoria che viene utilizzata dal thread RT.

Dalla documentazione, la mia comprensione è che questo può essere realizzato in 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;
}

Questo è l'unico posto nel programma (diverso setup iniziale) in cui viene modificato rt_data. Quando rt_data viene utilizzato nel contesto in tempo reale, esso viene copiato in un puntatore locale. Per old_d, in seguito, quando è sicuro che la vecchia memoria non viene utilizzata, verrà liberato nel filo non-RT. È corretto? Ho bisogno volatile da qualche parte? Ci sono altre primitive di sincronizzazione che dovrei essere chiamata?

Tra l'altro sto facendo questo in C ++, anche se sono interessati a sapere se la risposta è diversa per C.

Grazie in anticipo.

È stato utile?

Soluzione

In generale non utilizzare volatile quando si scrive codice concorrente in C/C++. La semantica di volatile sono così vicino a ciò che si desidera che è allettante, ma alla fine volatile è non basta . Purtroppo Java/C# volatile != C/C++ volatile. Herb Sutter ha un grande spiegare il pasticcio confuso.

Che cosa si vuole veramente è un recinto di memoria. __sync_lock_test_and_set prevede la recinzione per voi.

Sarà inoltre necessario un recinto di memoria quando si copia (carico) il puntatore rt_data alla copia locale.

Blocco programmazione libera è difficile. Se siete disposti a utilizzare C del Gcc ++ 0x estensioni, è un po 'più facile:

#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();
   /* ... */
}

Altri suggerimenti

Aggiorna : Questa risposta non è corretta, come mi manca il fatto che garantisce volatile che accede al volatile variabili non vengono riordinati, ma non fornisce tali garanzie rispetto ad altri non-volatile accessi e manipolazioni. Un recinto memoria non fornire tali garanzie, ed è necessaria per questa applicazione. La mia risposta originale è al di sotto, ma non agiscono su di esso. Vedi questa risposta per una buona spiegazione nel foro la mia comprensione che ha portato alla seguente risposta non corretta.

Risposta originale:

Si, è necessario volatile sulla vostra dichiarazione rt_data; volatile qualsiasi momento una variabile può essere modificata al di fuori del flusso di controllo di un filo accedervi, esso dovrebbe essere dichiarato . Mentre si può essere in grado di ottenere via senza volatile dal momento che si sta copiando a un puntatore locale volatile almeno aiuta con documentazione e inibisce anche alcune ottimizzazioni del compilatore che possono causare problemi. Si consideri il seguente esempio, adottata da DDJ :

volatile int a;
int b;
a = 1;
b = a;

Se è possibile per a a ha un valore cambiato tra a=1 e b=a, a quindi volatile dovrebbero essere dichiarati (a meno che, naturalmente, assegnando un valore out-of-data b è accettabile). Multithreading, in particolare con primitive atomiche, costituisce una tale situazione. La situazione viene attivato anche con le variabili modificate da gestori di segnale e da variabili mappate a locazioni di memoria dispari (ad esempio hardware I / O registri). Vedi anche questa domanda .

In caso contrario, si guarda bene a me.

In C, avrei probabilmente usare le primitive atomiche fornite da GLib per questo. Useranno un'operazione atomica dove disponibile e cadere di nuovo ad un implementazione lenta ma corretta mutex-based se le operazioni atomiche non sono disponibili. Boost può fornire qualcosa di simile per C ++.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top