Temukan cuplikan kode di bawah ini:
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;
}
Output yang saya dapatkan adalah:
Constructed : 0x7ffe27d1b0a4
Destroyed : 0x7ffe27d1b06c
Thread is joining...
Thread running at : 11
Destroyed : 0x2029c28
x : 1
Destroyed : 0x7ffe27d1b0a4
Saya bingung bagaimana destruktor dengan alamat 0x7ffe27d1b06c dan 0x2029c28 dipanggil dan tidak ada konstruktor yang dipanggil? Sedangkan konstruktor dan destruktor pertama dan terakhir masing-masing adalah dari objek yang saya buat.
c++
multithreading
destructor
SHAHBAZ
sumber
sumber
Jawaban:
Anda kehilangan instruksi pembuatan salinan dan memindahkan konstruksi. Modifikasi sederhana untuk program Anda akan memberikan bukti di mana konstruksi berlangsung.
Salin Pembuat
Output (alamat bervariasi)
Salin Konstruktor dan Pindahkan Konstruktor
Jika Anda memberikan pemindah dokumen, itu akan lebih disukai untuk setidaknya satu dari salinan tersebut:
Output (alamat bervariasi)
Referensi Dibungkus
Jika Anda ingin menghindari salinan itu, Anda dapat membungkus callable Anda dalam pembungkus referensi (
std::ref
). Karena Anda ingin memanfaatkant
setelah bagian threading selesai, ini layak untuk situasi Anda. Dalam praktiknya, Anda harus sangat berhati-hati ketika melakukan threading terhadap referensi untuk memanggil objek, karena umur objek harus meluas setidaknya selama utas menggunakan referensi.Output (alamat bervariasi)
Catat meskipun saya menyimpan copy-ctor dan move-ctor kelebihan, tidak ada yang dipanggil, karena pembungkus referensi sekarang adalah hal yang sedang disalin / dipindahkan; bukan hal itu referensi. Juga, pendekatan akhir ini memberikan apa yang mungkin Anda cari;
t.x
kembalimain
, pada kenyataannya, dimodifikasi menjadi11
. Itu tidak dalam upaya sebelumnya. Namun, tidak dapat cukup menekankan hal ini: berhati-hatilah melakukan ini . Masa objek sangat penting .Bergerak, Dan Tidak Ada Yang Lain
Akhirnya, jika Anda tidak tertarik untuk mempertahankan
t
seperti pada contoh Anda, Anda dapat menggunakan semantik bergerak untuk mengirim instance langsung ke utas, bergerak di sepanjang jalan.Output (alamat bervariasi)
Di sini Anda dapat melihat objek dibuat, referensi nilai untuk kata-sama kemudian dikirim langsung ke
std::thread::thread()
, di mana ia dipindahkan lagi ke tempat peristirahatan terakhir, yang dimiliki oleh utas dari titik itu ke depan. Tidak ada copy-ctors yang terlibat. Para pelaku sebenarnya menentang dua selongsong dan objek beton tujuan akhir.sumber
Adapun pertanyaan tambahan Anda diposting di komentar:
Konstruktor
std::thread
pertama membuat salinan argumen pertamanya (olehdecay_copy
) - di situlah copy konstruktor dipanggil. (Perhatikan bahwa dalam kasus argumen rvalue , sepertithread t1{std::move(t)};
atauthread t1{tFunc{}};
, pindahkan konstruktor akan dipanggil sebagai gantinya.)Hasilnya
decay_copy
adalah sementara yang berada di tumpukan. Namun, karenadecay_copy
dilakukan oleh utas panggilan , ini sementara berada di tumpukan dan dihancurkan di ujungstd::thread::thread
konstruktor. Akibatnya, sementara itu sendiri tidak dapat digunakan oleh utas yang baru dibuat secara langsung.Untuk "meneruskan" functor ke utas baru, objek baru harus dibuat di tempat lain , dan ini adalah tempat pemindah konstruktor dipanggil. (Jika tidak ada, copy constructor akan dipanggil sebagai gantinya.)
Perhatikan bahwa kita mungkin bertanya-tanya mengapa materialisasi sementara yang ditangguhkan tidak diterapkan di sini. Misalnya, dalam demo langsung ini , hanya satu konstruktor yang dipanggil, bukan dua. Saya percaya bahwa beberapa detail implementasi internal dari implementasi C ++ Standard library menghambat optimasi yang akan diterapkan untuk
std::thread
konstruktor.sumber