2011-08-16 11 views
7

Ho un parent e un thread di lavoro che condividono un flag bool e un vettore std ::. Il genitore legge solo (ad esempio, legge il bool o chiama my_vector.empty()); l'operaio scrive soloUn lettore. Uno scrittore. Alcune domande di carattere generale sui mutex e sui buildin atomici

Le mie domande:

  • Devo mutex proteggere la bandiera bool?

  • Posso dire che tutte le letture/scritture bool sono operazioni intrinsecamente atomiche? Se dici Sì o No, da dove hai preso le tue informazioni?

  • Recentemente ho sentito parlare di GCC Atomic-builtin. Posso usarli per rendere il mio flag in lettura/scrittura atomico senza dover usare i mutex? Qual è la differenza? Capisco che i builtin di Atomic si riducono al codice macchina, ma anche i mutex si riducono alle istruzioni della barriera di memoria della CPU, giusto? Perché le persone chiamano mutex un costrutto "OS-level"?

  • Ho bisogno di mutex proteggere il mio std :: vector? Ricordiamo che il thread operaio popola questo vettore, mentre il genitore chiama solo vuoto() su di esso (cioè lo legge solo)

  • Non credo che la protezione di mutex sia necessaria per il bool o per il vettore. Razionalizzo come segue, "Ok, se leggo la memoria condivisa poco prima che fosse aggiornata ... è ancora valido, avrò il valore aggiornato la prossima volta. Ancora più importante, non vedo perché lo scrittore debba essere bloccato mentre la lettura sta leggendo, perché dopotutto, il lettore sta solo leggendo! "

Se qualcuno può indicarmi la giusta direzione, sarebbe fantastico. Sono su GCC 4.3 e Intel x86 a 32 bit. Grazie mille!

risposta

13

Ho bisogno di mutex proteggere il flag bool?

Non necessariamente, un'istruzione atomica farebbe. Con atomic instruction intendo una funzione intrinseca del compilatore che a) impedisce il riordino/ottimizzazione del compilatore eb) i risultati in lettura/scrittura atomica ec) emette una barriera di memoria appropriata per garantire la visibilità tra le CPU (non necessario per le CPU x86 correnti che impiegano MESI cache coherency protocol). Simile a gcc atomic builtins.

Posso dire che tutte le letture/scritture bool sono operazioni intrinsecamente atomiche? Se dici Sì o No, da dove hai preso le tue informazioni?

Dipende dalla CPU. Per le CPU Intel - sì. Vedi Intel® 64 and IA-32 Architectures Software Developer's Manuals.

Recentemente ho sentito parlare di GCC Atomic-builtin.Posso usarli per rendere il mio flag in lettura/scrittura atomico senza dover usare i mutex? Qual è la differenza? Capisco che i builtin di Atomic si riducono al codice macchina, ma anche i mutex si riducono alle istruzioni della barriera di memoria della CPU, giusto? Perché le persone chiamano mutex un costrutto "OS-level"?

La differenza tra atomica e mutex è che quest'ultimo può sospendere il thread in attesa fino a quando il mutex non viene rilasciato. Con l'atomica puoi solo girare intensamente.

Ho bisogno di mutex proteggere il mio std :: vector? Ricordiamo che il thread di lavoro popola questo vettore, mentre il genitore chiama solo vuoto() su di esso (ad esempio, lo legge)

Sì.

Non credo che la protezione di mutex sia necessaria per il bool o per il vettore. Razionalizzo come segue, "Ok, se leggo la memoria condivisa poco prima che fosse aggiornata ... è ancora valido, avrò il valore aggiornato la prossima volta. Ancora più importante, non vedo perché lo scrittore debba essere bloccato mentre la lettura sta leggendo, perché dopotutto, il lettore sta solo leggendo! "

seconda dell'implementazione, vector.empty() possono comportare la lettura di due buffer di iniziare/puntatori finali e sottraendo o confrontandoli, quindi c'è la possibilità di leggere una nuova versione di un puntatore e una vecchia versione di un altro senza un mutex. Comportamento sorprendente può derivare.

+1

Per quanto riguarda il 'bool', ha certamente bisogno di qualcosa per garantire che l'accesso abbia effettivamente luogo quando pensa di farlo. Se ha un compilatore che supporta 'std :: atomic <>', allora dovrebbe essere sufficiente; in caso contrario, ha bisogno di una sorta di meccanismo di sincronizzazione o di altri mezzi per garantire che siano presenti le necessarie recinzioni, ecc. –

+0

Si noti inoltre che 'std :: vector' potrebbe spostare il suo contenuto da sotto i piedi quando è necessario riassegnarlo per un buffer più grande. A questo punto il consumatore potrebbe leggere 'cancella' la memoria. –

+0

@Maxim: Ho bisogno di un chiarimento per favore, secondo le operazioni manuali di Intel sui bool sono intrinsecamente atomici, eppure nella tua prima risposta dici che "un'istruzione atomica farebbe". Sei contraddittorio? Oppure, stai dicendo che dovrei essere dalla parte della sicurezza e utilizzare un built-in atomico in caso il mio codice venga eseguito su un sistema non Intel? – Kostolma

2

Risposte:

  1. Sarà necessario proteggere il bool (o qualsiasi altra variabile è per questo) che ha la possibilità di essere operati da due o più thread contemporaneamente. Puoi farlo con un mutex o operando sul bool atomicamente.
  2. Bool legge e scrive bool può essere operazioni atomiche, ma due operazioni sequenziali non sono certamente (ad esempio, una lettura e quindi una scrittura). Maggiori informazioni su questo più tardi.
  3. I builtin atomici forniscono una soluzione al problema precedente: la capacità di leggere e scrivere una variabile in un passaggio che non può essere interrotto da un altro thread. Questo rende l'operazione atomica.
  4. Se si utilizza il flag bool come 'mutex' (ovvero, solo il thread che imposta il flag bool su true ha il permesso di modificare il vettore), allora sei a posto. L'esclusione reciproca è gestita dal booleano, e fintanto che stai modificando il bool usando le operazioni atomiche, dovresti essere tutto pronto.
  5. Per rispondere a questo, mi permetta di usare un esempio:

 

bool    flag(false); 
std::vector<char> my_vector; 

while (true) 
{ 
    if (flag == false) // check to see if the mutex is owned 
    { 
     flag = true; // obtain ownership of the flag (the mutex) 

     // manipulate the vector 

     flag = false; // release ownership of the flag 
    } 
} 

Nel codice precedente in un ambiente multithread è possibile per il filo da preempted tra l'affermazione if (la leggi) e l'assegnazione (la scrittura), che significa che è possibile per due thread (o più) con questo tipo di codice sia per "possedere" il mutex (che i diritti per il vettore) allo stesso tempo. Questo è il motivo per cui le operazioni atomiche sono cruciali: assicurano che nello scenario sopra riportato il flag venga impostato solo da un thread alla volta, garantendo quindi che il vettore venga manipolato solo da un thread alla volta.

Nota che l'impostazione del flag su falso non deve essere un'operazione atomica perché questa istanza è l'unica con i diritti per modificarla.

A approssimativa (leggi: non testato) soluzione può sembrare qualcosa di simile:

bool    flag(false); 
std::vector<char> my_vector; 

while (true) 
{ 
    // check to see if the mutex is owned and obtain ownership if possible 
    if (__sync_bool_compare_and_swap(&flag, false, true)) 
    { 
     // manipulate the vector 

     flag = false; // release ownership of the flag 
    } 
} 

La documentazione per il comando incorporato atomica recita:

La versione “bool” restituisce true se il confronto ha successo e newval è stato scritto.

Il che significa che l'operazione verificherà se flag è falso e se è impostato su true. Se il valore era falso, viene restituito true, altrimenti false. Tutto questo avviene in una fase atomica, quindi è garantito che non venga anticipato da un altro thread.

0

Non ho le competenze per rispondere all'intera domanda, ma il tuo ultimo messaggio non è corretto nei casi in cui le letture non sono atomiche per impostazione predefinita.

Un interruttore di contesto può accadere ovunque, il lettore può ottenere il contesto passato a metà attraverso una lettura, lo scrittore può essere acceso e fare la scrittura completa, e quindi il lettore finirebbe la lettura. Il lettore non vedrebbe né il primo valore, né il secondo valore, ma potenzialmente un valore intermedio selvaggiamente inaccurato.

2

Dal punto di vista degli standard C++ 11, è necessario proteggere il bool con un mutex, o in alternativa utilizzare std::atomic<bool>. Anche quando sei sicuro che il tuo bool sia letto e scritto in modo atomico, c'è comunque la possibilità che il compilatore possa ottimizzare gli accessi ad esso perché non sa di altri thread che potrebbero potenzialmente accedervi.

Se per qualche ragione è assolutamente necessario l'ultimo bit di prestazioni della piattaforma, prendere in considerazione la lettura del "Manuale dello sviluppatore del software per architetture Intel 64 e IA-32", che ti spiegherà come funzionano le cose sotto la tua architettura. Ma ovviamente, questo renderà il tuo programma indenne.

Problemi correlati