Devo stare solo un momento, perché questo dovrebbe essere facile ma non riesco a farlo funzionare correttamente.Contatore atomico in gcc
Qual è il modo corretto di implementare un contatore atomico in GCC?
Ad esempio, voglio un contatore che va da zero a 4 ed è thread-safe.
stavo facendo questo (che è ulteriormente avvolto in una classe, ma non qui)
static volatile int _count = 0;
const int limit = 4;
int get_count(){
// Create a local copy of diskid
int save_count = __sync_fetch_and_add(&_count, 1);
if (save_count >= limit){
__sync_fetch_and_and(&_count, 0); // Set it back to zero
}
return save_count;
}
Ma è in esecuzione da 1 a 1-4 inclusiva poi intorno a zero.
Dovrebbe andare da 0 a 3. Normalmente farei un contatore con un operatore mod ma non lo so sapere come farlo in modo sicuro.
Forse questa versione è migliore. Riesci a vedere eventuali problemi con esso o offrire una soluzione migliore.
int get_count(){
// Create a local copy of diskid
int save_count = _count;
if (save_count >= limit){
__sync_fetch_and_and(&_count, 0); // Set it back to zero
return 0;
}
return save_count;
}
In realtà, vorrei sottolineare che non è assolutamente fondamentale che ogni thread ottiene un valore diverso. Se due thread hanno letto lo stesso valore nello stesso momento, questo non sarebbe un problema. Ma non possono superare il limite in qualsiasi momento.
Se __sync_fetch_and_add "esegue un'operazione atomica per invocazione" dipende dalla CPU - non specificato nella domanda. Potrebbe essere implementato secondo il tuo approccio "confronta e scambia", che è quello che ho usato su hardware Sun in passato (beh, l'implementazione del mio ex collega, con il nome simpatico di "atomic_robin" :-)). –
Non stavo parlando del numero di istruzioni eseguite; ci sono diversi modi per implementare lo scambio-add, ma sono tutti equivalenti purché in realtà scrivano solo in memoria ("commit") una volta. Il punto è che non è possibile costruire un "grande" primitivo atomico da diversi piccoli; loro non compongono. È possibile utilizzare più passaggi, ma il passaggio finale (commit) deve essere una singola operazione atomica che renda tutto visibile. Se al termine c'è più di un passo del genere, hai automaticamente una condizione di gara. –
Hey, grazie. Questo è esattamente quello che sto cercando. Non so esattamente cosa stavo pensando con la mia seconda soluzione. Immagino sia stata la mancanza di sonno a farmi scrivere un codice così buono. – Matt