Asumsikan saya memulai std::thread
dan kemudian detach()
, jadi utas terus mengeksekusi meskipun std::thread
yang pernah mewakilinya, keluar dari ruang lingkup.
Asumsikan lebih lanjut bahwa program tidak memiliki protokol yang dapat diandalkan untuk bergabung dengan utas yang dilepaskan 1 , sehingga utas yang terlepas masih berjalan saat main()
keluar.
Saya tidak dapat menemukan apa pun dalam standar (lebih tepatnya, dalam N3797 C ++ 14 draft), yang menggambarkan apa yang harus terjadi, baik 1,10 maupun 30,3 berisi kata-kata yang bersangkutan.
1 Pertanyaan lain, yang mungkin setara, adalah: "dapatkah utas terpisah dilepas lagi", karena protokol apa pun yang ingin Anda gabungkan, bagian pensinyalan harus dilakukan saat utas masih berjalan, dan penjadwal OS mungkin memutuskan untuk meletakkan utas untuk tidur selama satu jam tepat setelah pensinyalan dilakukan dengan tidak ada cara bagi pihak penerima untuk secara andal mendeteksi bahwa utas benar-benar selesai.
Jika kehabisan main()
dengan menjalankan thread terpisah menjalankan perilaku tidak terdefinisi, maka setiap penggunaan std::thread::detach()
perilaku tidak terdefinisi kecuali utas utama tidak pernah keluar 2 .
Dengan demikian, kehabisan main()
dengan thread terpisah berjalan harus memiliki efek yang ditentukan . Pertanyaannya adalah: di mana (dalam standar C ++ , bukan POSIX, bukan OS docs, ...) adalah efek yang didefinisikan.
2 Utas yang terlepas tidak dapat bergabung (dalam arti std::thread::join()
). Anda bisa menunggu hasil dari utas terpisah (mis. Melalui masa depan dari std::packaged_task
, atau dengan menghitung semaphore atau bendera dan variabel kondisi), tetapi itu tidak menjamin bahwa utas telah selesai dieksekusi . Memang, kecuali Anda meletakkan bagian pensinyalan ke dalam destruktor dari objek otomatis utas pertama, akan ada , secara umum, menjadi kode (destruktor) yang dijalankan setelah kode pensinyalan. Jika OS menjadwalkan utas utama untuk mengonsumsi hasil dan keluar sebelum utas terpisah selesai menjalankan kata destructor, apa yang akan didefinisikan untuk terjadi?
std::exit
atau keluar darimain
cukup, tetapi tidak perlu, untuk memenuhi persyaratan ini." (seluruh paragraf mungkin relevan) Lihat juga [support.start.term] / 8 (std::exit
dipanggil saatmain
kembali)Jawaban:
Jawaban untuk pertanyaan awal "apa yang terjadi pada utas terlepas ketika
main()
keluar" adalah:Ini terus berjalan (karena standar tidak mengatakan itu dihentikan), dan itu didefinisikan dengan baik, selama tidak menyentuh variabel (otomatis | thread_local) dari thread lain atau objek statis.
Ini tampaknya diizinkan untuk mengizinkan manajer thread sebagai objek statis (catatan di [basic.start.term] / 4 mengatakan sebanyak itu, terima kasih kepada @dyp untuk penunjuknya).
Masalah muncul ketika penghancuran objek statis telah selesai, karena kemudian eksekusi memasuki rezim di mana hanya kode yang diizinkan dalam penangan sinyal yang dapat dieksekusi ( [basic.start.term] / 1, kalimat pertama ). Dari pustaka standar C ++, itu hanya
<atomic>
pustaka ( [support.runtime] / 9, kalimat ke-2 ). Secara khusus, itu — secara umum — tidak termasukcondition_variable
(itu ditentukan implementasi apakah itu disimpan untuk digunakan dalam penangan sinyal, karena itu bukan bagian dari<atomic>
).Kecuali jika Anda telah melepaskan tumpukan Anda pada saat ini, sulit untuk melihat bagaimana menghindari perilaku yang tidak terdefinisi.
Jawaban untuk pertanyaan kedua "dapat memisahkan utas yang pernah bergabung lagi" adalah:
Ya, dengan
*_at_thread_exit
keluarga fungsi (notify_all_at_thread_exit()
,std::promise::set_value_at_thread_exit()
, ...).Seperti dicatat dalam catatan kaki [2] dari pertanyaan, memberi sinyal variabel kondisi atau semafor atau penghitung atom tidak cukup untuk bergabung dengan utas terpisah (dalam arti memastikan bahwa akhir pelaksanaannya) telah terjadi-sebelum menerima kata signaling oleh thread menunggu), karena, secara umum, akan ada lebih banyak kode yang dieksekusi setelah misalnya
notify_all()
dari variabel kondisi, khususnya destruktor objek otomatis dan thread-lokal.Menjalankan pensinyalan sebagai hal terakhir yang dilakukan thread ( setelah penghancur objek otomatis dan thread-lokal telah terjadi ) adalah tujuan
_at_thread_exit
dari rangkaian fungsi.Jadi, untuk menghindari perilaku tidak terdefinisi dengan tidak adanya jaminan implementasi di atas apa yang disyaratkan standar, Anda perlu (secara manual) bergabung dengan utas terpisah dengan
_at_thread_exit
fungsi melakukan pensinyalan atau membuat utas terpisah melepaskan hanya kode yang akan aman untuk penangan sinyal juga.sumber
exit
selesai menghancurkan objek statis, menjalankanatexit
penangan, pembilasan aliran dll. Ia mengembalikan kontrol ke lingkungan host, yaitu proses keluar. Jika utas terpisah masih berjalan (dan entah bagaimana menghindari perilaku yang tidak terdefinisi dengan tidak menyentuh apa pun di luar utasnya sendiri), maka itu hanya menghilang dalam kepulan asap saat proses keluar.main
panggilanpthread_exit
alih-alih kembali atau meneleponexit
maka itu akan menyebabkan proses untuk menunggu utas terlepas selesai, dan kemudian meneleponexit
setelah yang terakhir selesai.Melepaskan Utas
Menurut
std::thread::detach
:Dari
pthread_detach
:Melepaskan utas terutama untuk menghemat sumber daya, jika aplikasi tidak perlu menunggu utas selesai (mis. Daemon, yang harus dijalankan hingga proses penghentian):
std::thread
objek keluar dari ruang lingkup tanpa bergabung, yang biasanya mengarah pada panggilan untukstd::terminate()
dihancurkan.Thread Pembunuh
Perilaku penghentian proses sama dengan perilaku untuk utas utama, yang setidaknya bisa menangkap beberapa sinyal. Apakah thread lain dapat menangani sinyal tidak penting, karena seseorang dapat bergabung atau mengakhiri thread lain dalam doa penangan sinyal utama. (Pertanyaan terkait )
Seperti yang telah dinyatakan, utas apa pun, terlepas atau tidak, akan mati dengan prosesnya di sebagian besar OS . Proses itu sendiri dapat diakhiri dengan menaikkan sinyal, dengan menelepon
exit()
atau dengan kembali dari fungsi utama. Namun, C ++ 11 tidak dapat dan tidak mencoba untuk mendefinisikan perilaku yang tepat dari OS yang mendasarinya, sedangkan pengembang Java VM pasti dapat mengabstraksikan perbedaan tersebut sampai batas tertentu. AFAIK, proses eksotis dan model threading biasanya ditemukan pada platform kuno (yang C ++ 11 mungkin tidak akan porting) dan berbagai sistem embedded, yang dapat memiliki implementasi perpustakaan bahasa khusus dan / atau terbatas serta dukungan bahasa yang terbatas.Dukungan Thread
Jika utas tidak didukung
std::thread::get_id()
harus mengembalikan id yang tidak valid (dibangun secara defaultstd::thread::id
) karena ada proses sederhana, yang tidak memerlukan objek utas untuk dijalankan dan konstruktor astd::thread
harus melempar astd::system_error
. Ini adalah bagaimana saya memahami C ++ 11 dalam hubungannya dengan OS hari ini. Jika ada OS dengan dukungan threading, yang tidak menelurkan utas utama dalam prosesnya, beri tahu saya.Mengontrol Utas
Jika seseorang perlu menjaga kontrol atas utas untuk shutdown yang benar, seseorang dapat melakukannya dengan menggunakan sinkronisasi primitif dan / atau semacam bendera. Namun, dalam hal ini, pengaturan flag shutdown yang diikuti oleh join adalah cara yang saya sukai, karena tidak ada gunanya meningkatkan kompleksitas dengan melepaskan thread, karena sumber daya akan dibebaskan pada saat yang sama pula, di mana beberapa byte
std::thread
objek vs. kompleksitas yang lebih tinggi dan kemungkinan primitif yang lebih sinkron harus dapat diterima.sumber
Pertimbangkan kode berikut:
Menjalankannya pada sistem Linux, pesan dari thread_fn tidak pernah dicetak. OS memang membersihkan
thread_fn()
segera setelahmain()
keluar. Menggantit1.detach()
dengant1.join()
selalu mencetak pesan seperti yang diharapkan.sumber
Nasib utas setelah program keluar adalah perilaku yang tidak ditentukan. Tetapi sistem operasi modern akan membersihkan semua utas yang dibuat oleh proses penutupannya.
Saat melepaskan sebuah
std::thread
, tiga kondisi ini akan terus bertahan:*this
tidak lagi memiliki utasjoinable()
akan selalu sama denganfalse
get_id()
akan samastd::thread::id()
sumber
detach()
memiliki perilaku yang tidak terdefinisi? Sulit dipercaya ...Ketika utas utama (yaitu, utas yang menjalankan fungsi utama () berakhir, maka proses berakhir dan semua utas lainnya berhenti.
Referensi: https://stackoverflow.com/a/4667273/2194843
sumber
Untuk mengizinkan utas lain melanjutkan eksekusi, utas utama harus diakhiri dengan memanggil pthread_exit () daripada keluar (3). Tidak apa-apa menggunakan pthread_exit di main. Ketika pthread_exit digunakan, utas utama akan berhenti mengeksekusi dan akan tetap dalam status zombie (mati) sampai semua utas lainnya keluar. Jika Anda menggunakan pthread_exit di utas utama, tidak bisa mendapatkan status pengembalian utas lainnya dan tidak dapat melakukan pembersihan untuk utas lainnya (bisa dilakukan menggunakan pthread_join (3)). Juga, lebih baik lepaskan utas (pthread_detach (3)) sehingga sumber utas dilepaskan secara otomatis pada penghentian utas. Sumber daya bersama tidak akan dirilis sampai semua utas keluar.
sumber