Encuentre el fragmento de código a continuación:
class tFunc{
int x;
public:
tFunc(){
cout<<"Constructed : "<<this<<endl;
x = 1;
}
~tFunc(){
cout<<"Destroyed : "<<this<<endl;
}
void operator()(){
x += 10;
cout<<"Thread running at : "<<x<<endl;
}
int getX(){ return x; }
};
int main()
{
tFunc t;
thread t1(t);
if(t1.joinable())
{
cout<<"Thread is joining..."<<endl;
t1.join();
}
cout<<"x : "<<t.getX()<<endl;
return 0;
}
El resultado que obtengo es:
Constructed : 0x7ffe27d1b0a4
Destroyed : 0x7ffe27d1b06c
Thread is joining...
Thread running at : 11
Destroyed : 0x2029c28
x : 1
Destroyed : 0x7ffe27d1b0a4
Estoy confundido sobre cómo se llamaron los destructores con la dirección 0x7ffe27d1b06c y 0x2029c28 y no se llamó a los constructores. Mientras que el primer y el último constructor y destructor respectivamente son del objeto que creé.
c++
multithreading
destructor
SHAHBAZ
fuente
fuente
Respuestas:
Te falta instrumentar la copia de construcción y mover la construcción. Una modificación simple a su programa proporcionará evidencia de que es donde se están llevando a cabo las construcciones.
Copiar constructor
Salida (las direcciones varían)
Copiar constructor y mover constructor
Si proporciona un movimiento, se preferirá al menos una de esas copias:
Salida (las direcciones varían)
Referencia envuelta
Si desea evitar esas copias, puede envolver su llamada en un contenedor de referencia (
std::ref
). Dado que desea utilizart
después de que se realiza la parte de subprocesamiento, esto es viable para su situación. En la práctica, debe tener mucho cuidado al enhebrar contra referencias a objetos de llamada, ya que la vida útil del objeto debe extenderse al menos tanto como el hilo que utiliza la referencia.Salida (las direcciones varían)
Tenga en cuenta que aunque mantuve las sobrecargas copy-ctor y move-ctor, ninguno de los dos fue llamado, ya que el contenedor de referencia ahora es lo que se copia / mueve; No es lo que hace referencia. Además, este enfoque final ofrece lo que probablemente estaba buscando;
t.x
De vuelta enmain
, de hecho, se modifica a11
. No fue en los intentos anteriores. Sin embargo, no puedo enfatizar esto lo suficiente: tenga cuidado al hacer esto . La vida útil del objeto es crítica .Muévete, y nada más
Finalmente, si no tiene interés en retener
t
como lo ha hecho en su ejemplo, puede usar la semántica de movimiento para enviar la instancia directamente al hilo, moviéndose en el camino.Salida (las direcciones varían)
Aquí puede ver que se crea el objeto, la referencia de valor de dicho mismo luego se envía directamente a
std::thread::thread()
, donde se mueve nuevamente a su lugar de descanso final, propiedad del hilo desde ese punto en adelante. No hay copiadores involucrados. Los dtors reales están contra dos proyectiles y el objeto concreto de destino final.fuente
En cuanto a su pregunta adicional publicada en los comentarios:
El constructor de
std::thread
first crea una copia de su primer argumento (bydecay_copy
), que es donde se llama al constructor de copias . (Tenga en cuenta que en caso de un argumento rvalue , comothread t1{std::move(t)};
othread t1{tFunc{}};
, en su lugar, se llamaría al constructor move ).El resultado de
decay_copy
es un temporal que reside en la pila. Sin embargo, dado quedecay_copy
lo realiza un subproceso de llamada , este temporal reside en su pila y se destruye al final delstd::thread::thread
constructor. En consecuencia, el temporal en sí mismo no puede ser utilizado por un nuevo hilo creado directamente.Para "pasar" el functor al nuevo hilo, se necesita crear un nuevo objeto en otro lugar , y aquí es donde se invoca el constructor de movimiento . (Si no existiera, se invocaría el constructor de copia).
Tenga en cuenta que podríamos preguntarnos por qué la materialización temporal diferida no se aplica aquí. Por ejemplo, en esta demostración en vivo , solo se invoca un constructor en lugar de dos. Creo que algunos detalles de implementación interna de la implementación de la biblioteca estándar de C ++ dificultan la optimización que se aplicará al
std::thread
constructor.fuente