2011-02-25 17 views
30

Este es el ejemplo:Diferencia entre puntero y referencia como parámetro hilo

#include<iostream> 
#include<thread> 
using namespace std; 

void f1(double& ret) { 
    ret=5.; 
} 

void f2(double* ret) { 
    *ret=5.; 
} 

int main() { 
    double ret=0.; 
    thread t1(f1, ret); 
    t1.join(); 
    cout << "ret=" << ret << endl; 
    thread t2(f2, &ret); 
    t2.join(); 
    cout << "ret=" << ret << endl; 
} 

y la salida es:

ret=0 
ret=5 

compilado con gcc 4.5.2, con y sin -O2

¿Es este comportamiento esperado?

¿Esta carrera de datos del programa es gratuita?

Gracias

Respuesta

63

El constructor de std::thread deduce los tipos de argumento y los almacena por valor.

argumento de la función de plantilla C++ tipo mecanismo de deducción deduce tipo T de un argumento de tipo T&. Por lo tanto, todos los argumentos a std::thread se pasan por valor para que f1() y f2() siempre obtengan una copia.

Si usted insiste en el uso de una referencia, envuelva el argumento utilizando boost::ref() o std::ref():

thread t1(f1, boost::ref(ret)); 

O, si lo prefiere la simplicidad, pasar un puntero. Esto es lo que boost::ref() o std::ref() hacen por usted detrás de la escena.

+0

Respuesta correcta. Upvoted. –

+0

Esto realmente me ayudó, gracias +1. Si alguien nota aún este mensaje, le pregunto por qué 'thread' utiliza este mecanismo. Tiene algo que ver con c, o tiene una razón completamente diferente? – patrik

+1

@patrik La semántica del valor (copia) es segura en comparación con el almacenamiento de una referencia o un puntero. La referencia o un puntero pueden quedar colgando cuando el objeto al que se refieren se destruye. Esta es la razón por la cual los argumentos de la tienda 'std :: bind' y' std :: thread' están por valor de forma predeterminada y tienes que pedirle explícitamente que haga algo menos seguro. –

5

Si desea pasar parámetros por referencia a un std::thread debe incluir cada uno de ellos en std::ref:

thread t1(f1, std::ref(ret)); 

Más información here.

6

Que se requiera un std::ref() explícito (o boost::ref()) en estas situaciones es en realidad una característica de seguridad muy útil ya que pasar una referencia puede ser por naturaleza algo peligroso de hacer.

Con una referencia no constante, con frecuencia existe el peligro de que esté pasando una variable local, con una referencia constante podría ser temporal, y cuando está creando una función para llamar en un hilo diferente (y con bind en general, a menudo una función que se llamará más tarde/de forma asíncrona), tendrá el gran peligro de que el objeto ya no sea válido.

La vinculación se ve ordenada, pero estos errores son los más difíciles de encontrar, ya que el error se detecta (es decir, al invocar la función) no es el mismo lugar donde se cometió el error (en el momento del enlace) y puede ser es muy difícil calcular exactamente qué función se está llamando en ese momento y, por lo tanto, dónde estaba enlazada.

Es seguro en su instancia cuando se une al hilo en el ámbito de la variable que está pasando como referencia. Por lo tanto, cuando sabe que ese es el caso, hay un mecanismo para pasar una referencia.

No es una característica del lenguaje que me gustaría ver modificada, sobre todo porque es probable que exista una gran cantidad de código existente que confía en él haciendo una copia que se rompería si solo tomara como referencia automáticamente (y luego necesitaría una forma explícita de forzar una copia).

Cuestiones relacionadas