2010-02-12 14 views
9

Ho una classe a cui si accede da più thread. Entrambe le funzioni getter e setter sono protette da serrature. Le serrature per le funzioni getter sono necessarie? Perché?una funzione getter richiede un mutex?

class foo { 
public: 
    void setCount (int count) { 
     boost::lock_guard<boost::mutex> lg(mutex_); 
     count_ = count; 
    } 

    int count() { 
     boost::lock_guard<boost::mutex> lg(mutex_); // mutex needed? 
     return count_; 
    } 

private: 
    boost::mutex mutex_; 
    int count_; 
}; 

risposta

13

L'unico modo per aggirare il blocco è se riesci a convincerti che il sistema trasferirà la variabile protetta atomicamente in tutti i casi. Se non puoi esserne sicuro per un motivo o per un altro, allora avrai bisogno del mutex.

Per un tipo semplice come un int, potresti essere in grado di convincerti che questo è vero, a seconda dell'architettura e supponendo che sia allineato correttamente per il trasferimento a singola istruzione. Per ogni tipo che è più complicato di questo, devi avere il lucchetto.

4

Se non si dispone di un mutex intorno al getter, e un filo è una lettura, mentre un altro thread sta scrivendo di esso, si otterrà risultati divertenti.

+0

Non necessariamente. Leggere e scrivere un 'int' sono probabilmente operazioni atomiche. Naturalmente questo dipende dall'architettura e non è portatile. – Dan

+2

Esattamente - è un comportamento indefinito. È meglio avere l'overhead minuto di un lucchetto, in contrasto con la memoria corrotta. –

+0

Di solito, sì.Ma se hai una comprovata necessità di aumentare le prestazioni, potrebbe essere possibile omettere il blocco. Vedi la mia risposta. – Dan

2

in voi caso probabilmente no, se la CPU è a 32 bit, se count è un oggetto complesso o CPU ha bisogno di più di un'istruzione per aggiornare il suo valore, allora sì

5

È il mutex davvero solo proteggere un singolo int? Fa la differenza: se si tratta di un tipo di dati più complesso, è assolutamente necessario il blocco.

Ma se è solo un int, e si è certi che int è un tipo atomico (cioè, il processore non ha a che fare due memoria separata legge per caricare l'int in un registro), e vi hanno benchmark del prestazioni e determinato che hai bisogno di prestazioni migliori, quindi potresti prendere in considerazione la possibilità di rilasciare il blocco sia dal getter che dal setter. Se lo fai, assicurati di qualificare lo int come volatile. E scrivi un commento che spiega perché non hai la protezione mutex e in quali condizioni ti occorrerebbe se la classe cambi.

Inoltre, fate attenzione che non si dispone di codice come questo:

void func(foo &f) { 
    int temp = f.count(); 
    ++temp; 
    f.setCount(temp); 
} 

che non è threadsafe, indipendentemente dal fatto che si utilizza un mutex o meno. Se è necessario fare qualcosa del genere, la protezione mutex deve essere al di fuori delle funzioni setter/getter.

1

Il blocco è necessario per serializza l'accesso alla risorsa condivisa. Nel tuo caso specifico potresti scappare solo con lo atomic integer operations ma in generale, per oggetti più grandi che richiedono più di una transazione bus, hai bisogno di serrature per garantire che il lettore veda sempre un oggetto coerente.

1

Dipende dall'esatta implementazione dell'oggetto bloccato. Tuttavia, in generale, non vuoi che qualcuno modifichi (imposta?) Un oggetto mentre qualcun altro è in fase di lettura (ottenendo?). Il modo più semplice per evitare che ciò avvenga è bloccarlo.

Nelle configurazioni più complesse il blocco verrà implementato in modo tale che un numero qualsiasi di persone possa leggere contemporaneamente, ma nessuno può scrivere mentre qualcuno sta leggendo e nessuno può leggere mentre sta andando una scrittura.

Problemi correlati