c# - ज़ोंबी मौजूद हैं....NET में?




multithreading load (5)

1. क्या मैंने यहां बताया है कि "ज़ोंबी थ्रेड" की एक स्पष्ट परिभाषा है?

मैं सहमत हूं कि "ज़ोंबी थ्रेड" मौजूद है, यह एक शब्द है जो संसाधनों के साथ छोड़े गए थ्रेड के साथ होता है, जिसे वे जाने नहीं देते हैं और फिर भी पूरी तरह से मर नहीं जाते हैं, इसलिए नाम "ज़ोंबी" है, इसलिए आपका इस रेफरल की व्याख्या पैसे पर बहुत सही है!

2. Can zombie धागे .NET पर हो सकता है? (क्यों नहीं?)

हाँ वे हो सकते हैं। यह एक संदर्भ है, और वास्तव में विंडोज द्वारा "ज़ोंबी" के रूप में संदर्भित किया जाता है: एमएसडीएन मृत प्रक्रियाओं / धागे के लिए शब्द "ज़ोंबी" का उपयोग करता है

अक्सर यह एक और कहानी हो रही है, और आपकी कोडिंग तकनीकों और प्रथाओं पर निर्भर करती है, क्योंकि आपके लिए थ्रेड लॉकिंग की तरह और थोड़ी देर के लिए इसे किया है, मैं उस परिदृश्य के बारे में भी चिंता नहीं करता हूं।

और हां, जैसा टिप्पणियों में @ केविनपैंको ने सही ढंग से उल्लेख किया है, "ज़ोंबी थ्रेड" यूनिक्स से आते हैं, यही कारण है कि उनका उपयोग एक्सकोड-ऑब्जेक्टिव सी में किया जाता है और इसे "एनएसजेम्बी" कहा जाता है और डीबगिंग के लिए उपयोग किया जाता है। यह बहुत ही वैसे ही व्यवहार करता है ... केवल अंतर ही एक वस्तु है जिसे मरना चाहिए "ज़ोंबी थ्रेड" के बजाय डीबगिंग के लिए "ज़ोंबीऑब्जेक्ट" बन जाता है जो आपके कोड में संभावित समस्या हो सकती है।

मैं .NET में लॉक करने के बारे में एक टीम के साथी के साथ चर्चा कर रहा था। वह निचले स्तर और उच्च स्तरीय प्रोग्रामिंग दोनों में व्यापक पृष्ठभूमि वाला वास्तव में उज्ज्वल लड़का है, लेकिन निचले स्तर के प्रोग्रामिंग के साथ उसका अनुभव मेरे से कहीं अधिक है। वैसे भी, उन्होंने तर्क दिया कि एक सिस्टम को दुर्घटनाग्रस्त "ज़ोंबी थ्रेड" की स्वीकार्य छोटी संभावना से बचने के लिए यदि संभव हो तो भारी लोड के तहत होने वाली महत्वपूर्ण प्रणालियों पर .NET लॉकिंग से बचा जाना चाहिए। मैं नियमित रूप से लॉकिंग का उपयोग करता हूं और मुझे नहीं पता था कि "ज़ोंबी थ्रेड" क्या था, इसलिए मैंने पूछा। मुझे उनके स्पष्टीकरण से मिली छाप यह है कि एक ज़ोंबी धागा एक धागा है जिसे समाप्त कर दिया गया है लेकिन किसी भी तरह से कुछ संसाधनों पर अभी भी है। एक उदाहरण उन्होंने दिया कि कैसे ज़ोंबी धागा सिस्टम को तोड़ सकता है, थ्रेड कुछ ऑब्जेक्ट को लॉक करने के बाद कुछ प्रक्रिया शुरू करता है, और फिर लॉक को रिहा होने से पहले कुछ बिंदु समाप्त कर दिया जाता है। इस स्थिति में सिस्टम को क्रैश करने की क्षमता है, क्योंकि आखिरकार, उस विधि को निष्पादित करने के प्रयासों के परिणामस्वरूप थ्रेड सभी ऑब्जेक्ट तक पहुंचने की प्रतीक्षा कर रहे हैं जो कभी वापस नहीं आएगा, क्योंकि लॉक ऑब्जेक्ट का उपयोग करने वाला थ्रेड मर चुका है।

मुझे लगता है कि मुझे इसका सारांश मिला है, लेकिन अगर मैं आधार से बाहर हूं, तो कृपया मुझे बताएं। अवधारणा मुझे समझ में आया। मैं पूरी तरह से आश्वस्त नहीं था कि यह एक वास्तविक परिदृश्य था जो .NET में हो सकता है। मैंने पहले कभी "ज़ोंबी" के बारे में नहीं सुना है, लेकिन मुझे लगता है कि निचले स्तर पर गहराई से काम करने वाले प्रोग्रामर को मूलभूत कंप्यूटिंग (जैसे थ्रेडिंग) की गहरी समझ होती है। मैं निश्चित रूप से लॉकिंग में मान देखता हूं, और मैंने कई विश्व स्तरीय प्रोग्रामर लीवरेज लॉकिंग को देखा है। मेरे पास अपने लिए इसका मूल्यांकन करने की सीमित क्षमता भी है क्योंकि मुझे पता है कि lock(obj) कथन वास्तव में केवल सिंटैक्टिक चीनी है:

bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }

और क्योंकि Monitor.Enter और Monitor.ExitMonitor.Exit चिह्नित हैं। ऐसा लगता है कि .NET कुछ प्रकार की प्रसंस्करण करता है जो धागे को सिस्टम घटकों के संपर्क में रखने से बचाता है, जो इस तरह का प्रभाव डाल सकता है, लेकिन यह पूरी तरह से सट्टा है और शायद इस तथ्य के आधार पर कि मैंने कभी "ज़ोंबी धागे" के बारे में नहीं सुना है पहले। तो, मुझे उम्मीद है कि मैं यहां इस पर कुछ प्रतिक्रिया प्राप्त कर सकता हूं:

  1. क्या मैंने यहां बताया है कि "ज़ोंबी थ्रेड" की एक स्पष्ट परिभाषा है?
  2. ज़ोंबी धागे .NET पर हो सकता है? (क्यों नहीं?)
  3. यदि लागू हो, तो मैं .NET में ज़ोंबी थ्रेड के निर्माण को कैसे मजबूर कर सकता हूं?
  4. यदि लागू हो, तो मैं .NET में ज़ोंबी थ्रेड परिदृश्य को जोखिम के बिना लॉकिंग का लाभ कैसे उठा सकता हूं?

अद्यतन करें

मैंने दो साल पहले इस सवाल से थोड़ा पूछा। आज यह हुआ:


अभी मेरे अधिकांश उत्तरों को नीचे दी गई टिप्पणियों द्वारा सही किया गया है। मैं जवाब को मिटा नहीं दूंगा क्योंकि मुझे प्रतिष्ठा अंक चाहिए क्योंकि टिप्पणियों की जानकारी पाठकों के लिए मूल्यवान हो सकती है।

अमर ब्लू ने इंगित किया कि .NET 2.0 और finally ब्लॉक थ्रेड aborts के प्रति प्रतिरोधी हैं। और एंड्रियास निएडेरमेयर द्वारा टिप्पणी की गई, यह वास्तविक ज़ोंबी धागा नहीं हो सकता है, लेकिन निम्न उदाहरण दिखाता है कि धागे को कैसे रोकना समस्या पैदा कर सकता है:

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");
    }
}

हालांकि lock() { } ब्लॉक का उपयोग करते समय, finally तब भी निष्पादित किया जाएगा जब ThreadAbortException को उस तरह से निकाल दिया जाता है।

निम्न जानकारी, जैसा कि यह पता चला है, केवल .NET 1 और .NET 1.1 के लिए मान्य है:

यदि lock() { } अंदर एक अन्य अपवाद अवरुद्ध होता है, और ThreadAbortException अपवाद तब आता है जब finally ब्लॉक चलने वाला है, तो लॉक जारी नहीं होता है। जैसा कि आपने बताया है, lock() { } ब्लॉक को संकलित किया गया है:

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

यदि कोई अन्य धागा जेनरेटेड finally ब्लॉक के अंदर Thread.Abort() को कॉल करता है, तो लॉक जारी नहीं किया जा सकता है।


मैं आसानी से ज़ोंबी धागे बना सकते हैं।

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

यह थ्रेड हैंडल लीक ( Join() )। जहां तक ​​हम प्रबंधित दुनिया में चिंतित हैं, यह एक और स्मृति रिसाव है।

अब, एक धागे को इस तरह से मारना कि वास्तव में ताले रखता है, पीछे की ओर दर्द होता है लेकिन संभव है। अन्य लड़के का ExitThread() नौकरी करता है। जैसा कि उन्होंने पाया, फाइल हैंडल जीसी द्वारा साफ हो गया है लेकिन किसी ऑब्जेक्ट के चारों ओर एक lock नहीं होगा। लेकिन आप ऐसा क्यों करेंगे?


यह ज़ोंबी धागे के बारे में नहीं है, लेकिन पुस्तक प्रभावी सी # में IDISposable लागू करने पर एक अनुभाग है, (आइटम 17), जो ज़ोंबी ऑब्जेक्ट्स के बारे में बात करता है, जिसे मैंने सोचा था कि आपको दिलचस्प लगेगा।

मैं पुस्तक को स्वयं पढ़ने की सलाह देता हूं, लेकिन इसका अर्थ यह है कि यदि आपके पास कक्षा या तो आईडीस्पोज़ेबल लागू करने वाला है, या एक डिस्क्क्टरर है, तो केवल एक चीज जो आपको करना चाहिए वह संसाधन जारी कर रही है। यदि आप यहां अन्य चीजें करते हैं, तो एक मौका है कि ऑब्जेक्ट कचरा नहीं होगा, लेकिन किसी भी तरह से पहुंच योग्य नहीं होगा।

यह नीचे जैसा उदाहरण देता है:

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

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

जब इस वस्तु पर विनाशक को बुलाया जाता है, तो वैश्विक संदर्भ पर स्वयं का संदर्भ रखा जाता है, जिसका अर्थ है कि यह जीवित रहता है और कार्यक्रम के जीवन के लिए स्मृति में रहता है, लेकिन यह सुलभ नहीं है। इसका अर्थ यह हो सकता है कि संसाधन (विशेष रूप से अप्रबंधित संसाधन) पूरी तरह से जारी नहीं किए जा सकते हैं, जो सभी प्रकार के संभावित मुद्दों का कारण बन सकते हैं।

एक और पूरा उदाहरण नीचे है। जब तक फ़ोरैच लूप तक पहुंच जाता है, तब तक आपके पास एक छवि युक्त अंडेड सूची में 150 ऑब्जेक्ट होते हैं, लेकिन छवि जीसीड की गई है और यदि आप इसका उपयोग करने का प्रयास करते हैं तो आपको अपवाद मिलता है। इस उदाहरण में, जब मैं छवि को कुछ भी करने की कोशिश करता हूं और करता हूं, तो मैं एक ArgumentException (पैरामीटर मान्य नहीं है), चाहे मैं इसे सहेजने का प्रयास करता हूं, या ऊंचाई और चौड़ाई जैसे आयाम भी देखता हूं:

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://.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);
    }
}

दोबारा, मुझे पता है कि आप विशेष रूप से ज़ोंबी धागे के बारे में पूछ रहे थे, लेकिन प्रश्न का शीर्षक .net में लाश के बारे में है, और मुझे इसकी याद दिलाई गई और सोचा कि दूसरों को यह दिलचस्प लगेगा!


  • क्या मैंने यहां बताया है कि "ज़ोंबी थ्रेड" की एक स्पष्ट परिभाषा है?

मेरे लिए एक बहुत अच्छी व्याख्या की तरह लगता है - एक थ्रेड जो समाप्त हो गया है (और इसलिए अब कोई संसाधन जारी नहीं कर सकता), लेकिन जिसका संसाधन (जैसे हैंडल) अभी भी आसपास है और (संभावित रूप से) समस्याएं पैदा कर रहा है।

  • ज़ोंबी धागे .NET पर हो सकता है? (क्यों नहीं?)
  • यदि लागू हो, तो मैं .NET में ज़ोंबी थ्रेड के निर्माण को कैसे मजबूर कर सकता हूं?

वे निश्चित रूप से करते हैं, देखो, मैंने एक बनाया है!

[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);
    }
}

यह प्रोग्राम एक थ्रेड Target शुरू करता है जो एक फ़ाइल खोलता है और फिर तुरंत ExitThread का उपयोग कर खुद को मार ExitThreadपरिणामी ज़ोंबी धागा कभी भी "test.txt" फ़ाइल में हैंडल को रिलीज़ नहीं करेगा और इसलिए प्रोग्राम समाप्त होने तक फ़ाइल तब तक खुली रहेगी (आप प्रक्रिया एक्सप्लोरर या इसी तरह की जांच कर सकते हैं)। "Test.txt" को हैंडल तब तक जारी नहीं किया जाएगा जब तक कि GC.Collect कहा जाता है - यह पता चला है कि यह ज़ोंबी थ्रेड बनाने के लिए सोचा गया है जितना मुश्किल है)

  • यदि लागू हो, तो मैं .NET में ज़ोंबी थ्रेड परिदृश्य को जोखिम के बिना लॉकिंग का लाभ कैसे उठा सकता हूं?

जो मैंने अभी किया वह मत करो!

जब तक आपका कोड सही ढंग से साफ हो जाता है (अप्रबंधित संसाधनों के साथ काम करते हुए सुरक्षित हैंडल या समकक्ष कक्षाओं का उपयोग करें), और जब तक आप अजीब और अद्भुत तरीकों से धागे को मारने के अपने रास्ते से बाहर नहीं जाते (सुरक्षित तरीका बस है थ्रेड को कभी भी मारने के लिए - उन्हें सामान्य रूप से समाप्त करने दें, या यदि आवश्यक हो तो अपवादों के माध्यम से), एकमात्र तरीका है कि आप ज़ोंबी थ्रेड जैसा कुछ करने जा रहे हैं, अगर कुछ गलत हो गया है (उदाहरण के लिए सीएलआर में कुछ गलत हो जाता है)।

असल में यह वास्तव में एक ज़ोंबी धागा बनाने के लिए आश्चर्यजनक रूप से मुश्किल है (मुझे एक ऐसे फ़ंक्शन में पी / इनवॉक करना था जो आपको दस्तावेज़ीकरण में बताता है कि उसे सी के बाहर कॉल न करें)। उदाहरण के लिए निम्नलिखित (भयानक) कोड वास्तव में ज़ोंबी धागा नहीं बनाता है।

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);
}

कुछ बहुत भयानक गलतियों को करने के बावजूद, "test.txt" के लिए हैंडल अभी भी बंद हो जाता है जैसे ही Abort कहा जाता है (कवर के तहत SafeFileHandle हिस्से के रूप में SafeFileHandle हैंडल का उपयोग फ़ाइल फ़ाइल हैंडल को लपेटने के लिए करता है)

सीवेनहुइस उत्तर में लॉकिंग उदाहरण शायद एक संसाधन को रिलीज़ करने में विफल होने का सबसे आसान तरीका है (इस मामले में एक लॉक) जब एक थ्रेड को अजीब तरीके से समाप्त कर दिया जाता है, लेकिन इसके बजाय lock स्टेटमेंट का उपयोग करके आसानी से तय किया जाता है, या finally ब्लॉक में रिलीज डाल।

यह भी देखें







zombie-process