Saya sedang berdiskusi dengan rekan tim tentang mengunci .NET. Dia orang yang sangat cerdas dengan latar belakang yang luas dalam pemrograman level rendah dan level tinggi, tetapi pengalamannya dengan pemrograman level bawah jauh melebihi milikku. Lagi pula, Dia berpendapat bahwa .NET locking harus dihindari pada sistem kritis yang diharapkan berada di bawah beban berat jika mungkin untuk menghindari kemungkinan kecil dari "utas zombie" menabrak sistem. Saya secara rutin menggunakan penguncian dan saya tidak tahu apa itu "zombie thread", jadi saya bertanya. Kesan yang saya dapatkan dari penjelasannya adalah bahwa utas zombie adalah utas yang telah berakhir tetapi entah bagaimana masih berpegang pada beberapa sumber. Sebuah contoh yang dia berikan tentang bagaimana sebuah thread zombie dapat merusak suatu sistem adalah sebuah thread memulai beberapa prosedur setelah mengunci suatu objek, dan kemudian di beberapa titik diakhiri sebelum kunci dapat dilepaskan. Situasi ini berpotensi merusak sistem, karena pada akhirnya, upaya untuk mengeksekusi metode itu akan menghasilkan semua utas menunggu akses ke objek yang tidak akan pernah dikembalikan, karena utas yang menggunakan objek terkunci sudah mati.
Saya pikir saya mendapatkan intinya, tetapi jika saya tidak berada di tempat, tolong beri tahu saya. Konsep itu masuk akal bagi saya. Saya tidak sepenuhnya yakin bahwa ini adalah skenario nyata yang dapat terjadi di .NET. Saya belum pernah mendengar tentang "zombie", tetapi saya mengakui bahwa programmer yang telah bekerja secara mendalam di level yang lebih rendah cenderung memiliki pemahaman yang lebih dalam tentang dasar-dasar komputasi (seperti threading). Saya benar-benar melihat nilai dalam penguncian, dan saya telah melihat banyak pemrogram kelas dunia memanfaatkan penguncian. Saya juga memiliki kemampuan terbatas untuk mengevaluasi ini untuk diri saya sendiri karena saya tahu bahwa lock(obj)
pernyataan itu sebenarnya hanya gula sintaksis untuk:
bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }
dan karena Monitor.Enter
dan Monitor.Exit
ditandai extern
. Tampaknya dibayangkan bahwa .NET melakukan beberapa jenis pemrosesan yang melindungi utas dari paparan komponen sistem yang dapat memiliki dampak seperti ini, tetapi itu murni spekulatif dan mungkin hanya berdasarkan pada fakta bahwa saya belum pernah mendengar tentang "utas zombie" sebelum. Jadi, saya berharap saya bisa mendapatkan umpan balik tentang ini di sini:
- Apakah ada definisi yang lebih jelas tentang "utas zombie" daripada yang saya jelaskan di sini?
- Bisakah thread zombie muncul di .NET? (Kenapa / Mengapa tidak?)
- Jika berlaku, Bagaimana saya bisa memaksa pembuatan utas zombie di .NET?
- Jika berlaku, Bagaimana cara memanfaatkan penguncian tanpa risiko skenario untaian zombie di .NET?
Memperbarui
Saya mengajukan pertanyaan ini lebih dari dua tahun yang lalu. Hari ini terjadi:
sumber
wait
atauwaitpid
. Proses anak ini kemudian disebut "proses zombie". Lihat juga howtogeek.com/119815Jawaban:
Sepertinya penjelasan yang cukup bagus bagi saya - utas yang telah dihentikan (dan karenanya tidak dapat lagi mengeluarkan sumber daya apa pun), tetapi sumber dayanya (mis. Pegangan) masih ada dan (berpotensi) menyebabkan masalah.
Mereka yakin, lihat, saya membuat satu!
Program ini memulai utas
Target
yang membuka file dan kemudian langsung bunuh diri menggunakanExitThread
.Utas zombie yang dihasilkan tidak akan pernah melepaskan pegangan ke file "test.txt" sehingga file tersebut akan tetap terbuka sampai program berakhir (Anda dapat memeriksa dengan proses explorer atau serupa).Pegangan ke "test.txt" tidak akan dirilis sampaiGC.Collect
dipanggil - ternyata itu bahkan lebih sulit daripada yang saya pikirkan untuk membuat utas zombie yang bocor menangani)Jangan lakukan apa yang baru saja saya lakukan!
Selama kode Anda membersihkan sendiri dengan benar (gunakan Safe Handles atau kelas yang setara jika bekerja dengan sumber daya yang tidak dikelola), dan selama Anda tidak berusaha keras untuk mematikan utas dengan cara yang aneh dan luar biasa (cara teraman hanyalah untuk tidak pernah membunuh utas - biarkan mereka mengakhiri dirinya sendiri secara normal, atau melalui pengecualian jika perlu), satu-satunya cara Anda akan memiliki sesuatu yang menyerupai utas zombie adalah jika ada sesuatu yang sangat salah (misalnya ada yang salah dalam CLR).
Bahkan sebenarnya sangat sulit untuk membuat utas zombie (saya harus P / Invoke ke fungsi yang pada dasarnya memberitahu Anda dalam dokumentasi untuk tidak menyebutnya di luar C). Misalnya kode (mengerikan) berikut ini sebenarnya tidak membuat utas zombie.
Meskipun membuat beberapa kesalahan yang cukup mengerikan, pegangan untuk "test.txt" masih ditutup segera setelah
Abort
dipanggil (sebagai bagian dari finalizerfile
yang di bawah penutup menggunakan SafeFileHandle untuk membungkus pegangan file-nya)Contoh penguncian dalam jawaban C.Evenhuis mungkin adalah cara termudah untuk gagal melepaskan sumber daya (kunci dalam kasus ini) ketika utas diakhiri dengan cara yang tidak aneh, tapi itu mudah diperbaiki dengan menggunakan
lock
pernyataan sebagai gantinya, atau menempatkan rilis difinally
blok.Lihat juga
lock
kata kunci (tetapi hanya dalam. Net 3.5 dan sebelumnya)sumber
ExitThread
panggilan Anda . Jelas, itu berhasil, tetapi rasanya lebih seperti trik pintar daripada skenario realistis. Salah satu tujuan saya adalah mempelajari apa yang tidak boleh dilakukan sehingga saya tidak sengaja membuat utas zombie dengan kode .NET. Saya mungkin bisa menemukan bahwa memanggil kode C ++ yang diketahui menyebabkan masalah dari kode .NET akan menghasilkan efek yang diinginkan. Anda jelas tahu banyak tentang hal ini. Apakah Anda tahu ada kasus lain (mungkin aneh, tetapi tidak cukup aneh untuk tidak pernah terjadi tanpa sengaja) dengan hasil yang sama?Set excelInstance = GetObject(, "Excel.Application")
Saya sudah sedikit membersihkan jawaban saya, tetapi meninggalkan yang asli di bawah untuk referensi
Ini pertama kalinya saya mendengar istilah zombie jadi saya akan menganggap definisinya adalah:
Thread yang telah dihentikan tanpa melepaskan semua sumber dayanya
Jadi mengingat definisi itu, maka ya, Anda bisa melakukannya di .NET, seperti bahasa lain (C / C ++, java).
Namun , saya tidak berpikir ini sebagai alasan yang baik untuk tidak menulis thread, misi kode kritis dalam .NET. Mungkin ada alasan lain untuk memutuskan .NET tetapi menghapus .NET hanya karena Anda dapat memiliki utas zombie entah bagaimana tidak masuk akal bagi saya. Zombie thread dimungkinkan dalam C / C ++ (Saya bahkan berpendapat bahwa jauh lebih mudah untuk mengacaukannya dalam C) dan banyak aplikasi kritis yang di-thread dalam C / C ++ (perdagangan volume tinggi, basis data dll).
Kesimpulan Jika Anda sedang dalam proses menentukan bahasa yang akan digunakan, maka saya sarankan Anda mempertimbangkan gambaran besar: kinerja, keterampilan tim, jadwal, integrasi dengan aplikasi yang ada dll. Tentu saja, utas zombie adalah sesuatu yang harus Anda pikirkan , tetapi karena sangat sulit untuk benar-benar membuat kesalahan ini dalam. NET dibandingkan dengan bahasa lain seperti C, saya pikir kekhawatiran ini akan dibayangi oleh hal-hal lain seperti yang disebutkan di atas. Semoga berhasil!
Zombies Jawaban Asli † dapat ada jika Anda tidak menulis kode threading yang benar. Hal yang sama berlaku untuk bahasa lain seperti C / C ++ dan Java. Tapi ini bukan alasan untuk tidak menulis kode berulir di .NET.
Dan seperti halnya bahasa lain, ketahui harganya sebelum menggunakan sesuatu. Ini juga membantu untuk mengetahui apa yang terjadi di bawah tenda sehingga Anda dapat melihat kemungkinan adanya masalah.
Kode yang dapat diandalkan untuk sistem kritis misi tidak mudah untuk ditulis, bahasa apa pun yang Anda gunakan. Tapi saya yakin itu tidak mungkin dilakukan dengan benar di .NET. Juga AFAIK, .NET threading tidak jauh berbeda dengan threading di C / C ++, ia menggunakan (atau dibangun dari) panggilan sistem yang sama kecuali untuk beberapa konstruksi spesifik .net (seperti versi ringan dari RWL dan kelas acara).
† pertama kali saya mendengar istilah zombie tetapi berdasarkan deskripsi Anda, kolega Anda mungkin berarti utas yang diakhiri tanpa melepaskan semua sumber daya. Ini berpotensi menyebabkan kebuntuan, kebocoran memori atau efek samping buruk lainnya. Ini jelas tidak diinginkan tetapi dipilih. NET karena kemungkinan ini mungkin bukan ide yang baik karena itu mungkin dalam bahasa lain juga. Saya bahkan berpendapat bahwa lebih mudah untuk mengacaukan di C / C ++ daripada di. NET (terutama di C di mana Anda tidak memiliki RAII) tetapi banyak aplikasi penting ditulis dalam C / C ++ kan? Jadi itu sangat tergantung pada keadaan pribadi Anda. Jika Anda ingin mengekstrak setiap ons kecepatan dari aplikasi Anda dan ingin sedekat mungkin dengan bare metal, maka .NET mungkinbukan menjadi solusi terbaik. Jika Anda memiliki anggaran yang ketat dan melakukan banyak antarmuka dengan layanan web / perpustakaan .net yang ada / etc. NET mungkin merupakan pilihan yang baik.
sumber
extern
metode buntu . Jika Anda punya saran, saya ingin mendengarnya. (2) Saya setuju untuk melakukannya di .NET. Saya ingin percaya bahwa itu mungkin dengan mengunci, tetapi saya belum menemukan jawaban yang memuaskan untuk membenarkan bahwa hari inilocking
adalahlock
kata kunci, maka mungkin tidak karena serialisasi eksekusi. Untuk memaksimalkan throughput, Anda harus menggunakan konstruksi yang tepat tergantung pada karakteristik kode Anda. Saya akan mengatakan jika Anda dapat menulis kode yang dapat diandalkan di c / c ++ / java / apa pun menggunakan pthreads / boost / thread pools / apa pun, maka Anda dapat menulisnya di C # juga. Tetapi jika Anda tidak dapat menulis kode yang dapat diandalkan dalam bahasa apa pun menggunakan pustaka apa pun, maka saya ragu menulis dalam C # akan berbeda.Saat ini sebagian besar jawaban saya telah dikoreksi oleh komentar di bawah ini. Saya tidak akan menghapus jawaban
karena saya memerlukan poin reputasikarena informasi di komentar mungkin berharga bagi pembaca.Immortal Blue menunjukkan bahwa .NET 2.0 dan yang lebih
finally
tinggi kebal terhadap ulir dibatalkan. Dan seperti dikomentari oleh Andreas Niedermair, ini mungkin bukan utas zombie yang sebenarnya, tetapi contoh berikut menunjukkan bagaimana membatalkan utas dapat menyebabkan masalah:Namun ketika menggunakan
lock() { }
blok,finally
itu masih akan dieksekusi ketikaThreadAbortException
ditembakkan seperti itu.Informasi berikut ini, ternyata, hanya valid untuk .NET 1 dan .NET 1.1:
Jika di dalam
lock() { }
blok pengecualian lain terjadi, danThreadAbortException
tiba tepat ketikafinally
blok akan dijalankan, kunci tidak dilepaskan. Seperti yang Anda sebutkan,lock() { }
blok dikompilasi sebagai:Jika utas lain memanggil
Thread.Abort()
di dalamfinally
blok yang dibuat , kunci mungkin tidak dilepaskan.sumber
lock()
tetapi saya tidak dapat melihat penggunaan ini ... jadi - bagaimana ini terhubung? ini adalah "salah" penggunaanMonitor.Enter
danMonitor.Exit
(kurang penggunaantry
danfinally
)Monitor.Enter
danMonitor.Exit
tanpa penggunaan yang tepat dari -try
danfinally
, skenario Anda akan mengunci utas lainnya yang mungkin bertahan_lock
, jadi ada skenario deadlock - belum tentu thread zombie ... Juga, Anda tidak melepaskan kunci masukMain
... tapi, hei ... mungkin OP mengunci untuk kebuntuan, bukan benang-zombie :)lock
atau tepattry
/finally
-usageIni bukan tentang utas Zombie, tetapi buku Efektif C # memiliki bagian tentang penerapan IDisposable, (item 17), yang membahas tentang objek Zombie yang saya pikir mungkin menarik bagi Anda.
Saya sarankan membaca buku itu sendiri, tetapi intinya adalah jika Anda memiliki kelas yang mengimplementasikan IDisposable, atau mengandung Desctructor, satu-satunya hal yang harus Anda lakukan adalah mengeluarkan sumber daya. Jika Anda melakukan hal-hal lain di sini, maka ada kemungkinan bahwa objek tidak akan menjadi sampah yang dikumpulkan, tetapi juga tidak akan dapat diakses dengan cara apa pun.
Ini memberikan contoh yang mirip dengan di bawah ini:
Ketika destruktor pada objek ini dipanggil, referensi ke dirinya sendiri ditempatkan pada daftar global, yang berarti ia tetap hidup dan berada di memori selama umur program, tetapi tidak dapat diakses. Ini mungkin berarti bahwa sumber daya (terutama sumber daya yang tidak dikelola) mungkin tidak sepenuhnya dirilis, yang dapat menyebabkan segala macam masalah potensial.
Contoh yang lebih lengkap ada di bawah ini. Pada saat loop foreach tercapai, Anda memiliki 150 objek di daftar unduhan yang masing-masing berisi gambar, tetapi gambar telah GC'd dan Anda mendapatkan pengecualian jika Anda mencoba menggunakannya. Dalam contoh ini, saya mendapatkan ArgumentException (Parameter tidak valid) ketika saya mencoba dan melakukan apa pun dengan gambar, apakah saya mencoba menyimpannya, atau bahkan melihat dimensi seperti tinggi dan lebar:
Sekali lagi, saya sadar Anda bertanya tentang utas zombie khususnya, tetapi judul pertanyaannya adalah tentang zombie di .net, dan saya diingatkan tentang hal ini dan berpikir orang lain mungkin menganggapnya menarik!
sumber
_undead
statis?NullReferenceException
, karena saya merasa hal yang hilang harus lebih terikat ke mesin daripada ke aplikasi. Apakah ini benar?IDisposable
itu masalahnya. Saya mendapatkan perhatian dengan finalizer (destruktor) tetapi hanya karenaIDisposable
tidak akan membuat objek pergi ke antrian finalizer dan risiko skenario zombie ini. Peringatan ini menyangkut finalizer, dan mereka mungkin memanggil metode Buang. Ada contoh di manaIDisposable
digunakan dalam tipe tanpa finalizer. Sentimen seharusnya untuk pembersihan sumber daya, tetapi itu bisa berupa pembersihan sumber daya non-sepele. RX secara sah menggunakanIDisposable
untuk membersihkan langganan dan dapat memanggil sumber daya hilir lainnya. (Saya juga tidak memilih mundur ...)Pada sistem kritis di bawah beban berat, menulis kode bebas kunci lebih baik terutama karena peningkatan kinerja. Lihatlah hal-hal seperti LMAX dan bagaimana memanfaatkan "simpati mekanis" untuk diskusi hebat ini. Khawatir tentang utas zombie? Saya pikir itu adalah kasus tepi yang hanya bug yang harus diselesaikan, dan bukan alasan yang cukup baik untuk tidak menggunakannya
lock
.Kedengarannya lebih seperti temanmu yang hanya suka dan memamerkan pengetahuannya tentang terminologi eksotis yang tidak jelas bagiku! Sepanjang waktu saya menjalankan lab kinerja di Microsoft UK, saya tidak pernah menemukan contoh masalah ini di .NET.
sumber
lock
pernyataan itu secara tidak proporsional !lock
blok-blok besar berbutir lemak (relatif) mudah dipahami dengan baik. Saya akan menghindari memperkenalkan kompleksitas demi optimasi kinerja sampai Anda tahu Anda perlu.Saya setuju bahwa "Zombie Threads" ada, itu adalah istilah untuk merujuk pada apa yang terjadi dengan Threads yang tersisa dengan sumber daya yang mereka tidak lepaskan tetapi belum sepenuhnya mati, maka nama "zombie," jadi Anda Penjelasan referensi ini cukup tepat tentang uang!
Ya, itu bisa terjadi. Ini adalah referensi, dan sebenarnya disebut oleh Windows sebagai "zombie": MSDN menggunakan Word "Zombie" untuk proses / utas yang mati
Sering terjadi itu adalah cerita lain, dan tergantung pada teknik dan praktik pengkodean Anda, seperti untuk Anda yang menyukai Thread Locking dan telah melakukannya untuk sementara waktu, saya bahkan tidak akan khawatir tentang skenario yang terjadi pada Anda.
Dan Ya, seperti @KevinPanko benar disebutkan dalam komentar, "Zombie Threads" memang berasal dari Unix yang mengapa mereka digunakan dalam XCode-ObjectiveC dan disebut sebagai "NSZombie" dan digunakan untuk debugging. Berperilaku cukup banyak dengan cara yang sama ... satu-satunya perbedaan adalah objek yang seharusnya mati menjadi "ZombieObject" untuk debugging bukan "Zombie Thread" yang mungkin menjadi masalah potensial dalam kode Anda.
sumber
Saya bisa membuat utas zombie dengan cukup mudah.
Ini bocor pegangan utas (untuk
Join()
). Ini hanyalah kebocoran memori sejauh yang kami ketahui di dunia yang dikelola.Nah, membunuh benang dengan cara yang benar-benar memegang kunci adalah rasa sakit di bagian belakang tapi mungkin. Orang lain yang
ExitThread()
melakukan pekerjaan. Ketika ia menemukan, pegangan file dibersihkan oleh gc tetapi dilock
sekitar objek tidak. Tetapi mengapa Anda melakukan itu?sumber