2015-11-23 5 views
5

इसने वास्तव में मुझे दो बार काट दिया है। यदि आप इस तरह के सरल कोड करते हैं:नेट थ्रेड में हैंडल रिसाव

private void button1_Click(object sender, EventArgs e) 
    { 

     for (int i = 0; i < 1000000; i++) 
     { 
      Thread CE = new Thread(SendCEcho); 
      CE.Priority = ThreadPriority.Normal; 
      CE.IsBackground = true; 
      CE.Start(); 
      Thread.Sleep(500); 
      GC.Collect(); 
     } 
    } 

    private void SendCEcho() 
    { 
     int Counter = 0; 

     for (int i = 0; i < 5; i++) 
     { 
      Counter++; 
      Thread.Sleep(25); 
     } 
    } 

इस कोड को चलाएं और हैंडल उड़ें! थ्रेड। सो जाओ इसलिए आप इसे बंद कर सकते हैं और यह आपको कंप्यूटर पर नहीं ले जाता है। यह गारंटी देनी चाहिए कि अगली धागा लॉन्च होने से पहले थ्रेड लॉन्च हो जाता है। कॉलिंग जीसी। कोलेक्ट(); कुछ नहीं करता। मेरे अवलोकन से यह कोड सामान्य रीफ्रेश पर कार्य प्रबंधक को हर रीफ्रेश 10 हैंडल खो देता है।

इससे कोई फर्क नहीं पड़ता कि Send Sendcho() फ़ंक्शन में क्या है, यदि आप चाहें तो पांच की गणना करें। जब धागा मर जाता है तो वहां एक संभाल होता है जिसे साफ़ नहीं किया जाता है।

अधिकांश कार्यक्रमों के साथ यह वास्तव में कोई फर्क नहीं पड़ता क्योंकि वे विस्तारित अवधि के लिए नहीं चलते हैं। मैंने बनाए गए कुछ कार्यक्रमों पर उन्हें अंत में महीनों और महीनों तक चलने की जरूरत है।

यदि आप इस कोड को अधिक से अधिक व्यायाम करते हैं, तो अंततः आप उस बिंदु पर हैंडल कर सकते हैं जहां विंडोज ओएस अस्थिर हो जाता है और यह काम नहीं करेगा। रिबूट करने की आवश्यकता है।

मैं एक धागा पूल और इस तरह कोड का उपयोग करके समस्या का समाधान किया:

ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadJobMoveStudy)); 

मेरा प्रश्न हालांकि क्यों नेट में इस तरह के एक रिसाव है, और क्यों यह इतने लंबे समय के लिए ही अस्तित्व में है? 1.0 की तरह से? मुझे यहां जवाब नहीं मिला।

+2

आप हैंडल कैसे देख रहे हैं? –

+0

@Yacoub विंडोज़ टास्क मैनेजर में। यह गंभीरता से सभी उपलब्ध हैंडल को रिसाव करेगा और ओएस को अस्थिर बना देगा। – Jake

+0

@ pm100 .net मुझे ऐसा करने की अनुमति देता है। इस तरह थ्रेडिंग करने के लिए बहुत सारे ट्यूटोरियल दिखाए जा रहे हैं। मैं वादा करता हूं कि यदि आप इस कोड को ओवर-ओवर चलाते हैं, शून्य कार्य में कुछ भी नहीं डालते हैं, तो आप ओएस को अस्थिर बना देंगे। – Jake

उत्तर

3

GC.Collect() पर कॉल करने के बाद GC.WaitForPendingFinalizers(); जोड़ने का प्रयास करें; मुझे लगता है कि आपको वह मिल जाएगा जो आप के बाद हैं। नीचे पूर्ण स्रोत:

संपादित

इसके अलावा 2005 से इस पर एक नज़र डालें: https://bytes.com/topic/net/answers/106014-net-1-1-possibly-1-0-also-threads-leaking-event-handles-bug। आपके जैसा लगभग एक ही कोड है।

using System; 
using System.Threading; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     for (int i = 0; i < 1000000; i++) 
     { 
      Thread CE = new Thread(SendCEcho); 
      CE.Priority = ThreadPriority.Normal; 
      CE.IsBackground = true; 
      CE.Start(); 
      Thread.Sleep(500); 
      CE = null; 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
     } 
    } 

    public static void SendCEcho() 
    { 
     int Counter = 0; 
     for (int i = 0; i < 5; i++) 
     { 
      Counter++; 
      Thread.Sleep(25); 
     } 
    } 
} 
+0

ठीक है मैंने कुछ परीक्षण फिर से चलाए और ऐसा लगता है कि आप सही हैं। यदि मैं इस कोड को चलाता हूं तो मेरे पास अतिरिक्त GC.WaitForPendingFinalizers(); के साथ कोई हैंडल लीक नहीं है। तो सवाल उठता है कि मुझे स्पष्ट रूप से इस फ़ंक्शन को क्यों कॉल करना है या विंडोज संसाधनों को जारी नहीं करता है? मैंने सोचा कि कचरा संग्रह स्वचालित रूप से कम से कम अंततः होता है। जाहिर है, क्योंकि उस कॉल के बिना, यह अंततः ओएस को मार देता है। – Jake

+2

'संग्रह() 'संग्रह चलाएगा, लेकिन अंतिम वस्तुएं अंतिमकरण कतार पर रखी जाएंगी। उनके फाइनलर केवल भविष्य में किसी बिंदु पर अंतिम रूप से थ्रेड द्वारा चलाए जाएंगे। कॉलिंग 'WaitForPendingFinalizers()' जो आप उम्मीद करेंगे वह करता है: अंतिम थ्रेड को तब तक अवरुद्ध करता है जब तक कि फाइनलज़र थ्रेड इसकी बात न करे। – dlev

+0

@dlev इस तरह का एक सरल जवाब, लेकिन सही है। धन्यवाद! – Jake

5

आप ऐसे धागे बना रहे हैं जो कभी खत्म नहीं होते हैं। SendCEcho में लूप के लिए कभी समाप्त नहीं होता है, इसलिए धागे कभी खत्म नहीं होते हैं और इस प्रकार पुनः दावा नहीं किया जा सकता है। अगर मैं आपके लूप कोड को ठीक करता हूं तो थ्रेड खत्म हो जाते हैं और अपेक्षित के रूप में पुनः दावा किया जाता है। मैं नीचे दिए गए कोड के साथ समस्या को पुन: उत्पन्न करने में असमर्थ हूं।

static void SendCEcho() 
{ 
    int Counter = 0; 

    for (int i = 0; i < 5; i++) 
    { 
     Counter++; 
     Thread.Sleep(25); 
    } 
} 
+0

एक लूप कैसे पांच, सीमित, कभी खत्म नहीं हो सकता है? के लिए (int i = 0; i <5; i ++) ??? – Jake

+0

आपका प्रारंभिक कोड 'int (int i = 0; i <5 + i ++)' के लिए पढ़ा जाता है - जो कभी समाप्त नहीं होगा। आपका सही कोड संकलित नहीं करता है। यहां बिंदु यह है कि यह * करता है * थ्रेड विधि क्या करता है। –

5
for (int i = 0; i < 5 + i++;) 

काफी विचित्र टाइपो, तो आप अपने वास्तविक समस्या को फिर से बनाया है। एक है, एक थ्रेड ऑब्जेक्ट 5 ऑपरेटिंग सिस्टम हैंडल का उपभोग करता है, इसका आंतरिक फाइनलर उन्हें रिलीज़ करता है। एक .NET वर्ग सामान्यतः में यह सुनिश्चित करने के लिए एक निपटान() विधि है कि ऐसे हैंडल को जल्दी जारी किया जा सकता है लेकिन थ्रेड में कोई नहीं है। वह साहसी डिजाइन था, इस तरह का एक निपटान() विधि बहुत कॉल करने के लिए कठिन होगा।

इसलिए अंतिमकर्ता पर भरोसा करना एक कठिन आवश्यकता है। एक प्रोग्राम जिसमें "SendEcho" विधि है, और कुछ भी नहीं करता है, आप जोखिम को चला रहे हैं कि कचरा कलेक्टर कभी नहीं चलता है। तो फाइनलर अपना काम नहीं कर सकता है। जीसी.कोलेक्ट() स्वयं को कॉल करने के लिए यह आपके ऊपर है। आप शुरू करने वाले 1000 धागे, हर ऐसा कहने पर विचार करेंगे। या ThreadPool.QueueUserWorkItem() या Task.Run() का उपयोग करें ताकि आप धागे, तार्किक दृष्टिकोण को रीसायकल कर सकें।

यह सत्यापित करने के लिए कि Percmon.exe का उपयोग करें कि जीसी वास्तव में नहीं चलता है। अपने कार्यक्रम के लिए .NET CLR मेमोरी> # जेन 0 संग्रह काउंटर जोड़ें।

+0

क्षमा करें मैंने एक टाइपो बनाया है, लेकिन यह सिर्फ लूप के लिए है। – Jake

+0

हम पहले से ही जानते थे कि, बस बाकी पोस्ट पर ध्यान केंद्रित करें। –

संबंधित मुद्दे