2010-09-03 10 views
5

¿Es necesario usar un temporal aquí para que sea seguro para subprocesos?¿El retorno es atómico y debería usarlo temporalmente en getter para que sea seguro para subprocesos?

int getVal() { 
     this->_mutex.lock(); 
     int result = this->_val; 
     this->_mutex.unlock(); 
     return result; 
} 

te daré el desmontaje de la función simple prueba RAII

int test() 
{ 
    RAIITest raii; //let's say it's a scoped lock 
    return 3; 
} 


{ 
    0x004013ce <_Z4testv>: push %ebp 
    0x004013cf <_Z4testv+1>: mov %esp,%ebp 
    0x004013d1 <_Z4testv+3>: sub $0x28,%esp 
    return 3; 
    0x004013d4 <_Z4testv+6>: lea -0x18(%ebp),%eax 
    0x004013d7 <_Z4testv+9>: mov %eax,(%esp) 
    0x004013da <_Z4testv+12>: call 0x4167a0 <_ZN8RAIITestD1Ev> //here destructor is called 
    0x004013df <_Z4testv+17>: mov $0x3,%eax //here result is pushed onto the stack 
} 
0x004013e4 <_Z4testv+22>: leave 
0x004013e5 <_Z4testv+23>: ret 

el compilador gcc es/g ++ 3.4.5

+0

::: Accidentalmente, lea esto como C# y VTC. lo siento. – Brian

+0

No veo lo que su ejemplo de RAII se supone que debe probar. Al devolver una constante solo se le grita al compilador que está bien hacer trampa y optimizar porque el valor de retorno no se verá afectado. – jalf

Respuesta

5

Si el acceso a this->_val está sincronizado por this->_mutex, entonces usted don No puedo elegir la forma en que se escribe el código actualmente. Debe leer this->_val antes de desbloquear el mutex y debe desbloquear el mutex antes de regresar. La variable result es necesaria para obtener este orden de acciones.

Si usa un lock_guard (o scoped_lock en Boost), entonces no necesita usar el temporal porque el bloqueo en el mutex se liberará cuando la función regrese. Por ejemplo, utilizando la biblioteca de hilos C++ 0x:

int getVal() { 
    std::lock_guard<std::mutex> lock(_mutex); 
    return this->_val; 
} // lock is released by lock_guard destructor 
+0

No, no lo hará. Si this -> _ val es un resultado integrado será exactamente el mismo. Y el objeto (scoped_lock) se destruye antes de que el resultado se inserte en la pila. – doc

+2

@doc: ¿Qué no? La variable local 'lock' no se destruirá hasta que se realice la copia del objeto que se va a devolver. –

+0

eche un vistazo a disasembly He agregado mi pregunta – doc

1

Sí si usa el bloqueo explícito()/unlock(). No, si construyes un objeto de bloqueo en la pila y haces que su destructor lo desbloquee.

+0

¿por qué? El objeto se destruye antes de que se empuje el resultado debido a la devolución. – doc

+0

Ese no es el caso.Considere este código: std :: string f() { std :: string ret = "abc"; return ret; } Si 'ret' se destruyó antes de que return statement lo copiara como el valor de retorno de la función, f() podría devolver una basura, por ejemplo, si es que lo hace. – usta

+0

@usta es más fácil seleccionar el tipo intrínseco. Construcción, destrucción, estos son términos de alto nivel. El resultado detrás de escena se suele presionar en la pila de llamadas de función definida por un determinado ABI. Y lo que me molesta es que se llame al destructor de mutex antes de que el resultado esté allí. – doc

0

No: el compilador crea un valor temporal para el valor de retorno automáticamente. Normalmente, tampoco es necesario que proteja una lectura con mutex, por lo tanto, aunque sea multihilo, solo return _val; debería ser suficiente.

Sin embargo, me desharía del guión bajo principal: las reglas sobre qué nombres de variables puedes y no puedes usar cuando comienzan con un guión bajo son suficientemente complejas como para evitarlas por completo.

+0

Jerry tu comentario no necesita bloqueo para lectura porque esto es un int, ¿es correcto? Para los tipos más complejos, no desearía que las escrituras concurrentes continuasen al mismo tiempo que la construcción de copias, habría pensado. Gracias. –

+0

@Steve: Sí. Por supuesto, nada en el estándar dice que tiene que ser así (dado que el estándar no cubre el enhebrado), pero desde un punto de vista práctico, un int es esencialmente siempre atómico. –

+0

pero la "copia" de ese temporal ocurre después de que el mutex está desbloqueado. Si luego se suspenderá un hilo y el otro hilo modificará '_val' (pongo guiones bajos en este ejemplo para señalar que es un miembro de la clase (pero luego he agregado' this-> 'para señalarlo aún mejor) así que perdónenme) durante la "copia", entonces el resultado puede volverse potencialmente rubicundo. Sé que en la práctica esto no debería suceder, pero me gustaría saber qué es realmente seguro para subprocesos. – doc

0

Puede hacerlo limpiamente es su exclusión mutua se encapsula en un scoped_lock que abre en la destrucción:

int getVal() { 
     scoped_lock lockit(_mutex); 
     return _val; 
} 

Y sí, sí es necesario para mantener el bloqueo hasta que sea devuelto.

+0

En caso de que _val sea del tipo incorporado, el desmontaje se verá exactamente igual que en el caso de los bloqueos sin ámbito. – doc

Cuestiones relacionadas