Bagaimana cara mengakhiri utas di C ++ 11?

137

Saya tidak perlu menghentikan utas dengan benar, atau membuatnya menanggapi perintah "terminasi". Saya tertarik untuk menghentikan utas dengan paksa menggunakan C ++ 11 murni.

Alexander V
sumber
7
Berikut adalah pertanyaan bagus tentang topik ini: stackoverflow.com/questions/2790346/c0x-thread-interruptation "Semua spesifikasi bahasa mengatakan bahwa dukungan tidak dibangun ke dalam bahasa"
Nemanja Boric

Jawaban:

138
  1. Anda dapat menelepon std::terminate()dari utas apa pun dan utas yang Anda maksud akan dengan paksa berakhir.

  2. Anda bisa mengatur ~thread()agar dieksekusi pada objek utas target, tanpa campur tangan join()atau detach()pada objek itu. Ini akan memiliki efek yang sama dengan opsi 1.

  3. Anda bisa mendesain pengecualian yang memiliki destruktor yang melempar pengecualian. Dan kemudian mengatur utas utas untuk melemparkan pengecualian ini ketika akan diberhentikan dengan paksa. Bagian yang sulit dalam hal ini adalah mendapatkan utas target untuk membuang pengecualian ini.

Opsi 1 dan 2 tidak membocorkan sumber daya intra-proses, tetapi mereka mengakhiri setiap utas.

Opsi 3 mungkin akan membocorkan sumber daya, tetapi sebagian kooperatif karena utas target harus setuju untuk melemparkan pengecualian.

Tidak ada cara portabel di C ++ 11 (yang saya tahu) untuk secara non-kooperatif membunuh satu utas dalam program multi-utas (yaitu tanpa membunuh semua utas). Tidak ada motivasi untuk mendesain fitur seperti itu.

A std::threadmungkin memiliki fungsi anggota ini:

native_handle_type native_handle();

Anda mungkin dapat menggunakan ini untuk memanggil fungsi yang bergantung pada OS untuk melakukan apa yang Anda inginkan. Misalnya pada OS Apple, fungsi ini ada dan native_handle_typea pthread_t. Jika Anda berhasil, Anda kemungkinan akan membocorkan sumber daya.

Howard Hinnant
sumber
2
Nitpick sedikit pada "tidak bocor sumber intra-proses" : Meskipun tentu saja benar bahwa OS akan merebut kembali semua sumber daya setelah membunuh proses, sumber daya yang bocor sejauh program yang bersangkutan. Itu biasanya tidak relevan, tetapi mungkin masih menjadi masalah dalam beberapa kasus. std::terminatetidak memanggil destruktor statis juga tidak menyiram buffer output, sehingga urutan sumber daya yang dirilis tidak terdefinisi dengan baik, Anda juga tidak memiliki jaminan bahwa data Anda dapat dilihat oleh pengguna atau ditulis ke toko permanen, atau bahkan konsisten dan lengkap.
Damon
6
Anda juga dapat memanggil exit()atau abort()dengan efek keseluruhan yang sama.
n. 'kata ganti' m.
2
# 1 adalah lelucon dan @ChrisDodd benar. Lelucon ini dijelaskan dalam jawaban di kalimat pertama di bawah # 3. Lihat juga jawaban Nanno Langstraat dan komentar di bawahnya.
Howard Hinnant
43

Jawaban @Howard Hinnant benar dan komprehensif. Tetapi mungkin disalahpahami jika dibaca terlalu cepat, karena std::terminate()(seluruh proses) kebetulan memiliki nama yang sama dengan "terminating" yang ada dalam pikiran @AlexanderVX (1 utas).

Ringkasan: "terminasi 1 utas + secara paksa (utas target tidak bekerja sama) + murni C ++ 11 = Tidak mungkin."

Nanno Langstraat
sumber
14
Ahh, # 1 saya benar-benar lucu. Anda tidak harus menentukan utas mana yang ingin Anda akhiri dengan paksa. Sistem hanya secara ajaib tahu mana yang ingin Anda akhiri dan lakukan dengan paksa!
Howard Hinnant
8
Ya, std::terminate()jawabannya seperti kisah Djinn nakal klasik; itu memenuhi semua keinginan OP untuk surat itu, meskipun mungkin tidak dengan cara yang dia maksudkan . Humor yang bersahaja membuatku tersenyum. :-)
Nanno Langstraat
2
Hanya perlu mencegah siswa C ++ yang tidak bersalah mendapatkan harapan mereka terlalu jauh / terlalu lama.
Nanno Langstraat
2
Dinyatakan dengan elegan. :-)
Howard Hinnant
@NannoLangstraat dammnit ... Saya memiliki harapan yang sangat tinggi, sampai saya membaca: (..lol, oh well +1 ada di sekitar
:)
13

Pertanyaan ini sebenarnya memiliki sifat yang lebih dalam dan pemahaman yang baik tentang konsep multithreading secara umum akan memberi Anda wawasan tentang topik ini. Sebenarnya tidak ada bahasa atau sistem operasi apa pun yang memberikan Anda fasilitas untuk penghentian utas secara tiba-tiba tanpa peringatan untuk tidak menggunakannya. Dan semua lingkungan eksekusi ini sangat menyarankan pengembang atau bahkan memerlukan membangun aplikasi multithreading atas dasar pemutusan thread koperasi atau sinkron. Alasan untuk keputusan dan saran umum ini adalah bahwa semua itu dibangun di atas dasar model multithreading umum yang sama.

Mari kita bandingkan konsep multiprocessing dan multithreading untuk lebih memahami kelebihan dan keterbatasan yang kedua.

Multiprocessing mengasumsikan pemisahan seluruh lingkungan eksekusi ke dalam set proses yang sepenuhnya terisolasi yang dikendalikan oleh sistem operasi. Proses menggabungkan dan mengisolasi keadaan lingkungan eksekusi termasuk memori lokal dari proses dan data di dalamnya dan semua sumber daya sistem seperti file, soket, objek sinkronisasi. Isolasi adalah karakteristik yang sangat penting dari proses, karena membatasi penyebaran kesalahan oleh batas-batas proses. Dengan kata lain, tidak ada satu proses yang dapat mempengaruhi konsistensi proses lain dalam sistem. Hal yang sama berlaku untuk perilaku proses tetapi dengan cara yang tidak terlalu terbatas dan lebih kabur. Dalam lingkungan seperti itu setiap proses dapat dibunuh dalam momen "sewenang-wenang", karena pertama setiap proses diisolasi, kedua,

Sebaliknya, multithreading mengasumsikan menjalankan banyak utas dalam proses yang sama. Tetapi semua utas ini memiliki kotak isolasi yang sama dan tidak ada kontrol sistem operasi dari kondisi internal proses. Akibatnya setiap utas dapat mengubah status proses global dan juga merusaknya. Pada saat yang sama titik-titik di mana keadaan utas dikenal aman untuk mematikan utas sepenuhnya tergantung pada logika aplikasi dan tidak dikenal baik untuk sistem operasi maupun untuk runtime bahasa pemrograman. Sebagai akibatnya, penghentian utas pada saat yang sewenang-wenang berarti membunuhnya pada titik sewenang-wenang dari jalur pelaksanaannya dan dapat dengan mudah mengarah pada proses korupsi data yang luas, memori dan menangani kebocoran,

Karena ini, pendekatan umum adalah memaksa pengembang untuk menerapkan penghentian utas sinkron atau kooperatif, di mana satu utas dapat meminta penghentian utas lainnya dan utas lainnya dalam titik yang ditentukan dengan baik dapat memeriksa permintaan ini dan memulai prosedur penutupan dari kondisi yang ditentukan dengan baik dengan melepaskan semua sumber daya seluruh sistem global dan sumber daya seluruh proses lokal dengan cara yang aman dan konsisten.

ZarathustrA
sumber
8
Ini bukan jawaban yang sangat membantu. Multi-pemrosesan tidak relevan di sini, dan pengamatan tentang multi-threading agak luas.
MSalters
4
Apa yang ingin saya katakan dan katakan secara terperinci adalah bahwa model multithreading tidak menyediakan cara formal untuk pemutusan thread yang kuat. C ++ ingin mengikuti model yang jelas termasuk model memori, model multithreading dll. Metode terminasi ulir secara inheren tidak aman. Jika komite standar C ++ dipaksa untuk menambahkannya ke C ++, itu akan dibuat dengan pernyataan berikutnya "Metode terminasi () mengakhiri eksekusi thread. Perilaku tidak terdefinisi.", Yang akan terdengar seperti "membuat sihir dan (mungkin) mengakhiri utas ".
ZarathustrA
C # memiliki fitur ini.
Derf Skren
@ Der Skren "Sebenarnya tidak ada bahasa atau sistem operasi apa pun yang memberikan Anda fasilitas untuk penghentian utas secara tiba-tiba tanpa asinkron tanpa peringatan untuk tidak menggunakannya" (c) ZarathustrA "Komentar: Penting! Metode Thread.Bort harus digunakan dengan hati-hati Khususnya ketika Anda menyebutnya untuk membatalkan utas selain utas saat ini, Anda tidak tahu kode apa yang telah dijalankan atau gagal dijalankan ... "(c) tautan Microsoft: docs.microsoft.com/en-us/dotnet/ api / ...
ZarathustrA
11

Kiat menggunakan fungsi yang bergantung pada OS untuk mengakhiri utas C ++:

  1. std::thread::native_handle()hanya bisa mendapatkan jenis pegangan asli yang valid sebelum menelepon join()atau detach(). Setelah itu, native_handle()mengembalikan 0 - pthread_cancel()akan coredump.

  2. Untuk secara efektif memanggil fungsi penghentian utas asli (mis. pthread_cancel()), Anda perlu menyimpan gagang asli sebelum menelepon std::thread::join()atau std::thread::detach(). Sehingga terminator asli Anda selalu memiliki pegangan asli yang valid untuk digunakan.

Penjelasan lebih lanjut silakan merujuk ke: http://bo-yang.github.io/2017/11/19/cpp-kill-detached-thread .

boyang
sumber
5

Saya kira utas yang perlu dibunuh adalah apakah dalam mode menunggu apa pun, atau melakukan pekerjaan berat. Saya akan menyarankan menggunakan cara "naif".

Tetapkan beberapa boolean global:

std::atomic_bool stop_thread_1 = false;

Masukkan kode berikut (atau serupa) di beberapa titik kunci, dengan cara itu akan menyebabkan semua fungsi di tumpukan panggilan kembali sampai utas secara alami berakhir:

if (stop_thread_1)
    return;

Kemudian untuk menghentikan utas dari utas lain (utama):

stop_thread_1 = true;
thread1.join ();
stop_thread_1 = false; //(for next time. this can be when starting the thread instead)
Doron Ben-Ari
sumber