Apakah ada zombie ... di .NET?

385

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.Enterdan Monitor.Exitditandai 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:

  1. Apakah ada definisi yang lebih jelas tentang "utas zombie" daripada yang saya jelaskan di sini?
  2. Bisakah thread zombie muncul di .NET? (Kenapa / Mengapa tidak?)
  3. Jika berlaku, Bagaimana saya bisa memaksa pembuatan utas zombie di .NET?
  4. 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:

Objek dalam keadaan zombie.

smartcaveman
sumber
8
apakah Anda yakin rekan kerja Anda tidak berbicara tentang kebuntuan ??
Andreas Niedermair
10
@AndreasNiedermair - Saya tahu apa itu deadlocking dan itu jelas bukan masalah penyalahgunaan terminologi itu. Kebuntuan disebutkan dalam percakapan dan jelas berbeda dari "utas zombie". Bagi saya, perbedaan utama adalah bahwa kunci mati memiliki ketergantungan dua arah yang tidak dapat diselesaikan sedangkan benang zombie adalah satu arah, dan membutuhkan proses yang dihentikan. Jika Anda tidak setuju dan berpikir ada cara yang lebih baik untuk melihat hal-hal ini, tolong jelaskan
smartcaveman
19
Saya pikir istilah "zombie" sebenarnya berasal dari backgound UNIX, seperti dalam "proses zombie", bukan ??? Ada definisi yang jelas tentang "proses zombie" di UNIX: ini menggambarkan proses anak yang telah berakhir tetapi di mana orang tua dari proses anak masih perlu "melepaskan" proses anak (dan sumber dayanya) dengan menelepon waitatau waitpid. Proses anak ini kemudian disebut "proses zombie". Lihat juga howtogeek.com/119815
hogliux
9
Jika bagian dari program Anda macet, meninggalkan program dalam keadaan tidak terdefinisi, maka tentu saja itu dapat menyebabkan masalah dengan sisa program Anda. Hal yang sama dapat terjadi jika Anda menangani pengecualian secara tidak benar dalam program single-threaded. Masalahnya bukan dengan utas, masalahnya adalah Anda memiliki status global yang bisa berubah dan Anda tidak menangani penghentian utas secara tidak terduga. Rekan kerja Anda yang "benar-benar cerdas" sama sekali tidak berdasar pada yang satu ini.
Jim Mischel
9
"Sejak komputer pertama, selalu ada hantu di dalam mesin. Segmen acak kode yang telah dikelompokkan bersama untuk membentuk protokol yang tak terduga ..."
Chris Laplante

Jawaban:

242
  • Apakah ada definisi yang lebih jelas tentang "utas zombie" daripada yang saya jelaskan di sini?

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.

  • Bisakah thread zombie muncul di .NET? (Kenapa / Mengapa tidak?)
  • Jika berlaku, Bagaimana saya bisa memaksa pembuatan utas zombie di .NET?

Mereka yakin, lihat, saya membuat satu!

[DllImport("kernel32.dll")]
private static extern void ExitThread(uint dwExitCode);

static void Main(string[] args)
{
    new Thread(Target).Start();
    Console.ReadLine();
}

private static void Target()
{
    using (var file = File.Open("test.txt", FileMode.OpenOrCreate))
    {
        ExitThread(0);
    }
}

Program ini memulai utas Targetyang membuka file dan kemudian langsung bunuh diri menggunakan ExitThread. 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 sampai GC.Collectdipanggil - ternyata itu bahkan lebih sulit daripada yang saya pikirkan untuk membuat utas zombie yang bocor menangani)

  • Jika berlaku, Bagaimana cara memanfaatkan penguncian tanpa risiko skenario untaian zombie di .NET?

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.

static void Main(string[] args)
{
    var thread = new Thread(Target);
    thread.Start();
    // Ugh, never call Abort...
    thread.Abort();
    Console.ReadLine();
}

private static void Target()
{
    // Ouch, open file which isn't closed...
    var file = File.Open("test.txt", FileMode.OpenOrCreate);
    while (true)
    {
        Thread.Sleep(1);
    }
    GC.KeepAlive(file);
}

Meskipun membuat beberapa kesalahan yang cukup mengerikan, pegangan untuk "test.txt" masih ditutup segera setelah Abortdipanggil (sebagai bagian dari finalizer fileyang 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 lockpernyataan sebagai gantinya, atau menempatkan rilis di finallyblok.

Lihat juga

Justin
sumber
3
saya ingat ketika saya bermain dengan menyimpan barang-barang di excel menggunakan backgroundworker saya tidak merilis semua sumber daya sepanjang waktu (karena saya hanya melewatkan debugging dll). di taskmanager saya melihat setelahnya sekitar 50 proses excel. Apakah saya membuat proses zombieexcel?
Stefan
3
@Justin - +1 - Jawaban bagus. Saya agak skeptis dengan ExitThreadpanggilan 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?
smartcaveman
3
Jadi jawabannya adalah 'tidak dalam c # 4', kan? Jika Anda harus melompat keluar dari CLR untuk mendapatkan utas zombie, sepertinya bukan masalah .Net.
Gusdor
4
@Stefan saya akan mengatakan hampir pasti tidak. Saya telah melakukan apa yang Anda gambarkan berkali-kali. Proses Excel bukanlah zombie karena masih berjalan meskipun tidak dapat segera diakses melalui UI normal. Anda harus dapat mengambilnya melalui panggilan ke GetObject . Set excelInstance = GetObject(, "Excel.Application")
Daniel
7
"Utas zombie yang dihasilkan tidak akan pernah melepaskan pegangan ke file" test.txt "sehingga file tersebut akan tetap terbuka sampai program berakhir" salah. Bukti kecil: `static void Main (string [] args) {new Thread (GcCollect) .Start (); Utas baru (Target). Mulai (); Console.ReadLine (); } private static void Target () {using (var file = File.Open ("test.txt", FileMode.OpenOrCreate)) {ExitThread (0); }} private static void GcCollect () {while (true) {Thread.Sleep (10000); GC.Collect (); }} `
Sinix
46

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.

Jerahmeel
sumber
(1) Saya tidak yakin bagaimana mencari tahu apa yang terjadi di bawah tenda ketika saya menemukan externmetode 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 ini
smartcaveman
1
@smartcaveman jika yang Anda maksud lockingadalah lockkata 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.
Jerahmeel
@smartcaveman untuk mengetahui apa yang ada di balik tudung, google membantu menumpuk dan jika masalah Anda terlalu eksotis untuk ditemukan di web, reflektor sangat berguna. Tetapi untuk kelas threading, saya menemukan dokumentasi MSDN sangat berguna. Dan banyak dari itu hanya pembungkus untuk panggilan sistem yang sama yang Anda gunakan di Cway.
Jerahmeel
1
@smartcaveman tidak sama sekali, bukan itu yang saya maksud. Saya minta maaf jika itu terjadi seperti itu. Yang ingin saya katakan adalah bahwa Anda tidak boleh terlalu cepat untuk menghapus .NET saat menulis aplikasi penting. Tentu, Anda bisa melakukan hal-hal yang mungkin jauh lebih buruk daripada utas zombie (yang saya pikir hanyalah utas yang tidak merilis sumber daya yang tidak dikelola, yang dapat benar-benar terjadi dalam bahasa lain: stackoverflow.com/questions/14268080/… ), tetapi sekali lagi, itu tidak berarti bahwa .NET bukanlah solusi yang layak.
Jerahmeel
2
Saya tidak berpikir. NET adalah teknologi yang buruk sama sekali. Ini sebenarnya kerangka pengembangan utama saya. Tapi, karena ini, saya pikir penting bagi saya untuk memahami kekurangan yang rentan. Pada dasarnya, saya memilih .NET, tetapi karena saya menyukainya, bukan karena saya tidak. (Dan jangan khawatir, aku + 1-ed kamu)
smartcaveman
26

Saat ini sebagian besar jawaban saya telah dikoreksi oleh komentar di bawah ini. Saya tidak akan menghapus jawaban karena saya memerlukan poin reputasi karena informasi di komentar mungkin berharga bagi pembaca.

Immortal Blue menunjukkan bahwa .NET 2.0 dan yang lebih finallytinggi 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:

class Program
{
    static readonly object _lock = new object();

    static void Main(string[] args)
    {
        Thread thread = new Thread(new ThreadStart(Zombie));
        thread.Start();
        Thread.Sleep(500);
        thread.Abort();

        Monitor.Enter(_lock);
        Console.WriteLine("Main entered");
        Console.ReadKey();
    }

    static void Zombie()
    {
        Monitor.Enter(_lock);
        Console.WriteLine("Zombie entered");
        Thread.Sleep(1000);
        Monitor.Exit(_lock);
        Console.WriteLine("Zombie exited");
    }
}

Namun ketika menggunakan lock() { }blok, finallyitu masih akan dieksekusi ketika ThreadAbortExceptionditembakkan seperti itu.

Informasi berikut ini, ternyata, hanya valid untuk .NET 1 dan .NET 1.1:

Jika di dalam lock() { }blok pengecualian lain terjadi, dan ThreadAbortExceptiontiba tepat ketika finallyblok akan dijalankan, kunci tidak dilepaskan. Seperti yang Anda sebutkan, lock() { }blok dikompilasi sebagai:

finally 
{
    if (lockWasTaken) 
        Monitor.Exit(temp); 
}

Jika utas lain memanggil Thread.Abort()di dalam finallyblok yang dibuat , kunci mungkin tidak dilepaskan.

C.Evenhuis
sumber
2
Anda berbicara tentang lock()tetapi saya tidak dapat melihat penggunaan ini ... jadi - bagaimana ini terhubung? ini adalah "salah" penggunaan Monitor.Enterdan Monitor.Exit(kurang penggunaan trydan finally)
Andreas Niedermair
4
Saya tidak akan menyebut bahwa zombie-thread - ini hanyalah penggunaan yang salah Monitor.Enterdan Monitor.Exittanpa penggunaan yang tepat dari - trydan finally, skenario Anda akan mengunci utas lainnya yang mungkin bertahan _lock, jadi ada skenario deadlock - belum tentu thread zombie ... Juga, Anda tidak melepaskan kunci masuk Main... tapi, hei ... mungkin OP mengunci untuk kebuntuan, bukan benang-zombie :)
Andreas Niedermair
1
@AndreasNiedermair mungkin definisi utas zombie tidak sesuai dengan yang saya pikirkan. Mungkin kita dapat menyebutnya "utas yang telah menghentikan eksekusi tetapi belum merilis semua sumber daya". Tergoda untuk menghapus & melakukan pekerjaan rumah saya, tetapi tetap menjaga jawaban untuk kemiripan dengan skenario OP.
C.Evenhuis
2
jangan tersinggung di sini! sebenarnya jawaban Anda membuat saya berpikir tentang hal itu :) karena OP secara eksplisit berbicara tentang kunci tidak dilepaskan, saya percaya rekannya berbicara tentang penguncian mati - karena kunci bukanlah sumber daya nyata yang terikat pada utas ( itu dibagikan ... Nona) - dan karena itu setiap "zombie" benang bisa dihindari dengan tetap berpegang praktek-praktek terbaik dan menggunakan lockatau tepat try/ finally-usage
Andreas Niedermair
1
@ C.Evenhuis - Saya tidak yakin definisi saya akurat. Bagian dari mengapa saya mengajukan pertanyaan adalah untuk menjernihkan hal ini. Saya pikir konsep ini disebut dalam C / C ++
smartcaveman
24

Ini 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:

internal class Zombie
{
    private static readonly List<Zombie> _undead = new List<Zombie>();

    ~Zombie()
    {
        _undead.Add(this);
    }
}

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:

class Program
{
    static void Main(string[] args)
    {
        for (var i = 0; i < 150; i++)
        {
            CreateImage();
        }

        GC.Collect();

        //Something to do while the GC runs
        FindPrimeNumber(1000000);

        foreach (var zombie in Zombie.Undead)
        {
            //object is still accessable, image isn't
            zombie.Image.Save(@"C:\temp\x.png");
        }

        Console.ReadLine();
    }

    //Borrowed from here
    //http://stackoverflow.com/a/13001749/969613
    public static long FindPrimeNumber(int n)
    {
        int count = 0;
        long a = 2;
        while (count < n)
        {
            long b = 2;
            int prime = 1;// to check if found a prime
            while (b * b <= a)
            {
                if (a % b == 0)
                {
                    prime = 0;
                    break;
                }
                b++;
            }
            if (prime > 0)
                count++;
            a++;
        }
        return (--a);
    }

    private static void CreateImage()
    {
        var zombie = new Zombie(new Bitmap(@"C:\temp\a.png"));
        zombie.Image.Save(@"C:\temp\b.png");
    }
}

internal class Zombie
{
    public static readonly List<Zombie> Undead = new List<Zombie>();

    public Zombie(Image image)
    {
        Image = image;
    }

    public Image Image { get; private set; }

    ~Zombie()
    {
        Undead.Add(this);
    }
}

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!

JMK
sumber
1
Ini menarik. Apakah ini _undeadstatis?
smartcaveman
1
Jadi saya mencobanya, dengan beberapa cetak dari objek "hancur". Itu memperlakukannya seperti benda normal. Adakah yang bermasalah dengan melakukan ini?
smartcaveman
1
Saya telah memperbarui jawaban saya dengan contoh yang semoga menunjukkan masalah ini dengan lebih jelas.
JMK
1
Saya tidak tahu mengapa Anda terpilih. Saya merasa terbantu. Inilah pertanyaan - pengecualian apa yang akan Anda dapatkan? Saya tidak berharap itu akan menjadi NullReferenceException, karena saya merasa hal yang hilang harus lebih terikat ke mesin daripada ke aplikasi. Apakah ini benar?
smartcaveman
4
Tentunya bukan IDisposableitu masalahnya. Saya mendapatkan perhatian dengan finalizer (destruktor) tetapi hanya karena IDisposabletidak 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 mana IDisposabledigunakan dalam tipe tanpa finalizer. Sentimen seharusnya untuk pembersihan sumber daya, tetapi itu bisa berupa pembersihan sumber daya non-sepele. RX secara sah menggunakan IDisposableuntuk membersihkan langganan dan dapat memanggil sumber daya hilir lainnya. (Saya juga tidak memilih mundur ...)
James World
21

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.

James World
sumber
2
Saya pikir rangkaian pengalaman yang berbeda membuat kita paranoid tentang bug yang berbeda. Dia mengakui itu kasus tepi ekstrem saat menjelaskannya, tetapi jika itu benar-benar tepi-kasus dan bukan tidak pernah terjadi, saya ingin setidaknya memahaminya sedikit lebih baik - Terima kasih atas masukan Anda
smartcaveman
1
Cukup adil. Saya hanya tidak ingin Anda khawatir tentang lockpernyataan itu secara tidak proporsional !
James World
1
Saya tidak akan terlalu khawatir jika saya memiliki pemahaman yang lebih dalam tentang masalah ini.
smartcaveman
4
Saya terbalik karena Anda mencoba untuk menyingkirkan beberapa thread-FUD, yang ada terlalu banyak. Lock-FUD akan mengambil lebih banyak pekerjaan untuk dibuang :) Saya hanya merasa ngeri ketika dev mengambil spinlock tanpa batas, (untuk kinerja), sehingga mereka dapat menyalin 50 ribu data ke antrian yang lebar.
Martin James
3
Ya, baik secara umum maupun khusus. Menulis kode bebas kunci yang benar sama sulitnya dengan pemrograman. Dalam pengalaman saya untuk sebagian besar kasus, lockblok-blok besar berbutir lemak (relatif) mudah dipahami dengan baik. Saya akan menghindari memperkenalkan kompleksitas demi optimasi kinerja sampai Anda tahu Anda perlu.
James World
3

1. Apakah ada definisi yang lebih jelas tentang "utas zombie" daripada apa yang saya jelaskan di sini?

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!

2.Bisakah thread zombie muncul di .NET? (Kenapa / Mengapa tidak?)

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.

SH
sumber
1
Tetapi cara MSDN menggunakan zombie sangat berbeda dari cara pertanyaan ini menggunakannya.
Ben Voigt
1
Oh ya saya setuju, tapi saya menegaskan bahwa MSDN bahkan menyebut utas sebagai Zombie Utas saat mati. dan bahwa mereka sebenarnya dapat terjadi.
SH
4
Tentu, tapi ini mengacu pada beberapa kode lain yang masih memegang pegangan ke utas, bukan pegangan pegangan ke sumber daya saat keluar. Kalimat pertama Anda, menyetujui definisi dalam pertanyaan, adalah apa yang salah.
Ben Voigt
1
Ahh, aku mengerti maksudmu. Sejujurnya aku bahkan tidak menyadari itu. Saya memfokuskan pada poinnya bahwa definisi itu ada, terlepas dari bagaimana hal itu terjadi. Ingatlah bahwa definisi itu ada karena apa yang terjadi pada utas, dan bukan bagaimana hal itu dilakukan.
SH
0

Saya bisa membuat utas zombie dengan cukup mudah.

var zombies = new List<Thread>();
while(true)
{
    var th = new Thread(()=>{});
    th.Start();
    zombies.Add(th);
}

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 di locksekitar objek tidak. Tetapi mengapa Anda melakukan itu?

Joshua
sumber