2010-02-12 10 views
9

Tengo una clase a la que se accede desde varios subprocesos. Ambas funciones getter y setter están protegidas con bloqueos. ¿Realmente se necesitan los bloqueos para las funciones getter? ¿Por qué?tiene una función getter necesita 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_; 
}; 

Respuesta

13

La única forma de evitar el bloqueo es si puede convencerse de que el sistema transferirá atómicamente la variable protegida en todos los casos. Si no puede estar seguro de eso por una razón u otra, entonces necesitará el mutex.

Para un tipo simple como un int, puede convencerse de que esto es cierto, dependiendo de la arquitectura, y suponiendo que está alineado correctamente para la transferencia de una sola instrucción. Para cualquier tipo que sea más complicado que esto, vas a tener que tener el candado.

4

Si usted no tiene un mutex alrededor del captador, y un hilo está leyendo mientras que otro hilo está escribiendo, usted obtendrá resultados divertidos.

+0

No necesariamente. Leer y escribir un 'int' son probablemente operaciones atómicas. Por supuesto, esto depende de la arquitectura y no es portátil. – Dan

+2

Exactamente: es un comportamiento indefinido. Es mejor tener la sobrecarga de un candado, a diferencia de la memoria que se corrompe. –

+0

Por lo general, sí.Pero si tiene una necesidad demostrada de aumentar el rendimiento, es posible omitir el bloqueo. Ver mi respuesta – Dan

2

en que el caso probablemente no, si su CPU es de 32 bits, sin embargo si el recuento es un objeto complejo o CPU necesita más de una instrucción para actualizar su valor, entonces sí

5

¿Es el mutex realmente sólo la protección de una sola int? Hace la diferencia: si se trata de un tipo de datos más complejo, definitivamente necesitas bloquearlo.

Pero si es sólo un int, y que está seguro de que int es un tipo atómica (es decir, el procesador no tiene que hacer dos memoria independiente lee para cargar el int en un registro), y se han referenciado el rendimiento y determinó que necesita un mejor rendimiento, entonces puede considerar dejar caer el bloqueo tanto del captador como del colocador. Si lo hace, asegúrese de calificar al int como volatile. Y escriba un comentario explicando por qué no tiene protección mutex, y bajo qué condiciones lo necesitaría si la clase cambia.

Además, tenga cuidado de que no dispone de código como este:

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

que no está multihebra, independientemente de si utiliza un mutex o no. Si necesita hacer algo como eso, la protección de mutex tiene que estar fuera de las funciones setter/getter.

1

El bloqueo es necesario para serializar el acceso al recurso compartido. En su caso específico, puede salirse con la suya solo con atomic integer operations pero, en general, para objetos más grandes que requieren más de una transacción de bus, necesita bloqueos para garantizar que el lector siempre vea como un objeto consistente.

1

Depende de la implementación exacta del objeto que se está bloqueando. Sin embargo, en general, no desea que alguien modifique (configure?) Un objeto mientras otra persona está en el proceso de leerlo (¿consiguiendo?). La forma más fácil de prevenir eso es hacer que un lector lo bloquee.

En configuraciones más complicadas, el bloqueo se implementará de tal forma que cualquier número de personas pueda leer a la vez, pero nadie puede escribir mientras nadie está leyendo, y nadie puede leer mientras se está escribiendo.

Cuestiones relacionadas