2013-03-30 14 views
6

Recentemente ho appreso che i compilatori ottimizzano il codice riorganizzando le istruzioni e che questo può essere controllato utilizzando le barriere.Ho bisogno di un recinto o di una barriera o qualcosa del genere quando i blocchi/sblocchi mutex sono sepolti in profondità nelle chiamate di funzione?

IIRC, il blocco di un mutex crea una barriera e lo sblocco di un mutex crea anche una barriera, per impedire che il codice all'interno della sezione critica esca.

Quindi pthread_mutex_lock e pthread_mutex_unlock devono essere implicitamente queste "barriere". Cosa succede se ho una classe come questa, che avvolge il mio mutex?

class IMutex { 
public: 
    virtual void lock() = 0; 
    virtual void unlock() = 0; 
}; 

mi sembra, il compilatore non sapranno che sto chiamando pthread_mutex_lock() all'interno di blocco(), e pthread_mutex_unlock() all'interno di sblocco(), perché è tutto virtual'd via.

Questo porterà a bug? Devo specificare manualmente le barriere in qualche modo?

+2

Penso che una chiamata di funzione sarebbe un ostacolo al codice in movimento del compilatore. Usiamo mutex e non aggiungiamo barriere. Ci sono barriere di memoria che sono una cosa diversa. –

+0

oh, questo ha senso. Grazie! – Verdagon

+0

Sì, nessun compilatore sensato riordina intorno a una chiamata di funzione virtuale ... –

risposta

6

Le istruzioni di riordino vengono eseguite su vari livelli. Il più ovvio è il compilatore e uno meno ovvio è la CPU (che è al volo). Tuttavia, le funzioni di sincronizzazione sono quasi sempre una fence, che impedisce che le istruzioni prima e dopo la fence vengano riordinate.

Quindi se il telefono virtuale lock chiama pthread_mutex_*(), le funzioni virtuali contengono una fence.

Quindi la risposta breve è: No, non porterà a bug.

C'è anche la parola chiave volatile, che a seconda della piattaforma può anche generare una barriera. Tuttavia, l'utilizzo delle parole chiave volatili rende molto più difficile individuare tali recinti poiché ogni volta che si utilizza una funzione o una variabile che è instabile, si introduce una barriera. Quindi il consiglio è di usare le funzioni di sincronizzazione della piattaforma.

L'unica volta che è necessario essere a conoscenza di recinzioni è quando non si utilizzano oggetti di concorrenza per eseguire la sincronizzazione (come l'utilizzo di un bool invece di un mutex).

+1

Non consiglierei volatile sui membri dati, in quanto potrebbe introdurre una recinzione di memoria che, ancora una volta, potrebbe nascondere un bug di corsa dati a causa del mutex "effettivo" avendo qualche altro bug ... Usa le funzioni di sincronizzazione corrette invece di volatile. – ActiveTrayPrntrTagDataStrDrvr

+0

Grazie a @ActiveTrayPrntrTagDataStrDrvr Ho aggiornato la risposta per includere la tua raccomandazione. – DevWouter