Das Code-Snippet finden Sie unten:
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;
}
Die Ausgabe, die ich bekomme, ist:
Constructed : 0x7ffe27d1b0a4
Destroyed : 0x7ffe27d1b06c
Thread is joining...
Thread running at : 11
Destroyed : 0x2029c28
x : 1
Destroyed : 0x7ffe27d1b0a4
Ich bin verwirrt, wie die Destruktoren mit den Adressen 0x7ffe27d1b06c und 0x2029c28 aufgerufen wurden und keine Konstruktoren aufgerufen wurden. Während der erste und der letzte Konstruktor bzw. Destruktor von dem Objekt sind, das ich erstellt habe.
c++
multithreading
destructor
SHAHBAZ
quelle
quelle
Antworten:
Ihnen fehlt die Instrumentenkopie- und Verschiebungskonstruktion. Eine einfache Änderung Ihres Programms liefert Hinweise darauf, wo die Konstruktionen stattfinden.
Konstruktor kopieren
Ausgabe (Adressen variieren)
Konstruktor kopieren und Konstruktor verschieben
Wenn Sie einen Verschiebungscode angeben, wird dieser für mindestens eine dieser ansonsten Kopien bevorzugt:
Ausgabe (Adressen variieren)
Referenz verpackt
Wenn Sie diese Kopien vermeiden möchten, können Sie Ihren Callable in einen Referenz-Wrapper (
std::ref
) einschließen . Da Siet
nach dem Einfädeln den Teil verwenden möchten, ist dies für Ihre Situation sinnvoll. In der Praxis müssen Sie beim Threading gegen Referenzen zum Aufrufen von Objekten sehr vorsichtig sein, da die Lebensdauer des Objekts mindestens so lange dauern muss, wie der Thread die Referenz verwendet.Ausgabe (Adressen variieren)
Beachten Sie, dass, obwohl ich die Überladungen copy-ctor und move-ctor beibehalten habe, keine aufgerufen wurden, da der Referenz-Wrapper jetzt das zu kopierende / verschobene Objekt ist. nicht das, worauf es sich bezieht. Dieser endgültige Ansatz liefert auch das, wonach Sie wahrscheinlich gesucht haben.
t.x
back inmain
ist in der Tat modifiziert zu11
. Es war nicht in den vorherigen Versuchen. Ich kann das jedoch nicht genug betonen: Sei vorsichtig dabei . Die Objektlebensdauer ist kritisch .Bewegen Sie sich und nichts als
Wenn Sie kein Interesse daran
t
haben, wie in Ihrem Beispiel beizubehalten, können Sie die Verschiebungssemantik verwenden, um die Instanz direkt an den Thread zu senden und sich dabei zu bewegen.Ausgabe (Adressen variieren)
Hier können Sie sehen, dass das Objekt erstellt wird. Der r-Wert-Verweis auf das gleiche Objekt wird dann direkt an gesendet
std::thread::thread()
, wo es erneut an seinen endgültigen Ruheplatz verschoben wird, der dem Thread von diesem Punkt an gehört. Es sind keine Kopierer beteiligt. Die tatsächlichen Dtoren sind gegen zwei Schalen und das konkrete Endzielobjekt.quelle
Wie für Ihre zusätzliche Frage in den Kommentaren gepostet:
Der Konstruktor von
std::thread
first erstellt eine Kopie seines ersten Arguments (vondecay_copy
) - dort wird der Kopierkonstruktor aufgerufen. (Beachten Sie, dass im Falle eines rvalue Argument, wiethread t1{std::move(t)};
oderthread t1{tFunc{}};
, bewegen Konstruktor stattdessen aufgerufen werden.)Das Ergebnis von
decay_copy
ist eine temporäre Datei, die sich auf dem Stapel befindet. Da diesdecay_copy
jedoch von einem aufrufenden Thread ausgeführt wird , befindet sich dieser temporäre Thread auf seinem Stapel und wird am Ende desstd::thread::thread
Konstruktors zerstört. Folglich kann das Temporär selbst nicht von einem neu erstellten Thread direkt verwendet werden.Um den Funktor an den neuen Thread zu "übergeben", muss ein neues Objekt an einer anderen Stelle erstellt werden , und hier wird der Verschiebungskonstruktor aufgerufen. (Wenn es nicht vorhanden wäre, würde stattdessen der Kopierkonstruktor aufgerufen.)
Beachten Sie, dass wir fragen sich vielleicht , warum latente temporäre Materialisierung hier nicht angewendet wird. In dieser Live-Demo wird beispielsweise nur ein Konstruktor anstelle von zwei aufgerufen. Ich glaube, dass einige interne Implementierungsdetails der Implementierung der C ++ Standard-Bibliothek die Anwendung für den
std::thread
Konstruktor behindern .quelle