Veuillez trouver l'extrait de code ci-dessous:
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;
}
La sortie que j'obtiens est:
Constructed : 0x7ffe27d1b0a4
Destroyed : 0x7ffe27d1b06c
Thread is joining...
Thread running at : 11
Destroyed : 0x2029c28
x : 1
Destroyed : 0x7ffe27d1b0a4
Je suis confus comment les destructeurs avec l'adresse 0x7ffe27d1b06c et 0x2029c28 ont été appelés et aucun constructeur n'a été appelé? Alors que le premier et le dernier constructeur et destructeur sont respectivement de l'objet que j'ai créé.
c++
multithreading
destructor
SHAHBAZ
la source
la source
Réponses:
Il vous manque une construction de copie et de déplacement de l'instrumentation. Une simple modification de votre programme fournira la preuve que c'est là que les constructions ont lieu.
Copier le constructeur
Sortie (les adresses varient)
Copier le constructeur et déplacer le constructeur
Si vous fournissez un ctor de déplacement, il sera préférable pour au moins une de ces copies:
Sortie (les adresses varient)
Référence enveloppé
Si vous voulez éviter ces copies, vous pouvez envelopper votre callable dans un wrapper de référence (
std::ref
). Étant donné que vous souhaitez utiliser unet
fois la partie de filetage terminée, cela est viable pour votre situation. Dans la pratique, vous devez être très prudent lors du filetage par rapport aux références à des objets d'appel, car la durée de vie de l'objet doit s'étendre au moins aussi longtemps que le fil utilisant la référence.Sortie (les adresses varient)
Notez que même si j'ai conservé les surcharges copy-ctor et move-ctor, aucune n'a été appelée, car le wrapper de référence est maintenant la chose copiée / déplacée; pas la chose à laquelle il fait référence. De plus, cette approche finale fournit ce que vous recherchiez probablement;
t.x
retourmain
est, en fait, modifié en11
. Ce n'était pas dans les tentatives précédentes. Je ne saurais trop insister sur cela, cependant: soyez prudent . La durée de vie des objets est critique .Bouge, et rien que
Enfin, si vous n'avez aucun intérêt à conserver
t
comme vous l'avez fait dans votre exemple, vous pouvez utiliser la sémantique de déplacement pour envoyer l'instance directement au thread, en cours de route.Sortie (les adresses varient)
Ici, vous pouvez voir que l'objet est créé, la référence rvalue à ladite-même puis envoyée directement à
std::thread::thread()
, où il est à nouveau déplacé vers son lieu de repos final, propriété du thread à partir de ce point. Aucun copieur n'est impliqué. Les dtors réels sont contre deux obus et l'objet concret de destination finale.la source
Quant à votre question supplémentaire publiée dans les commentaires:
Le constructeur de
std::thread
first crée une copie de son premier argument (pardecay_copy
) - c'est là que le constructeur de copie est appelé. (Notez que dans le cas d'un argument rvalue , tel quethread t1{std::move(t)};
orthread t1{tFunc{}};
, le constructeur de déplacement serait appelé à la place.)Le résultat de
decay_copy
est un temporaire qui réside sur la pile. Cependant, comme ildecay_copy
est effectué par un thread appelant , ce temporaire réside sur sa pile et est détruit à la fin dustd::thread::thread
constructeur. Par conséquent, le temporaire lui-même ne peut pas être utilisé directement par un nouveau thread créé.Pour «passer» le foncteur au nouveau thread, un nouvel objet doit être créé ailleurs , et c'est là que le constructeur de déplacement est appelé. (S'il n'existait pas, le constructeur de copie serait appelé à la place.)
Notez que nous pouvons nous demander pourquoi la matérialisation temporaire différée n'est pas appliquée ici. Par exemple, dans cette démo en direct , un seul constructeur est invoqué au lieu de deux. Je crois que certains détails d'implémentation interne de l'implémentation de la bibliothèque C ++ Standard entravent l'optimisation à appliquer pour le
std::thread
constructeur.la source