क्या TPL टास्क ऑब्जेक्ट पर Dispose () को कॉल नहीं करना स्वीकार्य है?


123

मैं बैकग्राउंड थ्रेड पर चलने के लिए एक कार्य को ट्रिगर करना चाहता हूं। मैं कार्यों के पूरा होने पर इंतजार नहीं करना चाहता।

3.5 .net में मैंने ऐसा किया होगा:

ThreadPool.QueueUserWorkItem(d => { DoSomething(); });

.Net 4 में TPL सुझाया गया तरीका है। मैंने जो सामान्य पैटर्न देखा है वह अनुशंसित है:

Task.Factory.StartNew(() => { DoSomething(); });

हालाँकि, StartNew()विधि एक Taskऑब्जेक्ट देता है जो लागू करता है IDisposable। यह उन लोगों द्वारा अनदेखा किया गया लगता है जो इस पैटर्न की सिफारिश करते हैं। Task.Dispose()विधि पर MSDN प्रलेखन कहता है:

"टास्क के लिए अपना अंतिम संदर्भ जारी करने से पहले हमेशा डिस्पोज़ को कॉल करें।"

जब तक यह पूरा न हो जाए, तब तक आप किसी कार्य को डिस्पोज़ नहीं कर सकते, इसलिए मुख्य थ्रेड प्रतीक्षा और कॉल डिस्पोज़ पहले स्थान पर एक बैकग्राउंड थ्रेड पर करने के बिंदु को पराजित करेगा। वहाँ भी कोई पूर्ण / समाप्त होने वाली घटना नहीं लगती है जिसका उपयोग सफाई के लिए किया जा सकता है।

टास्क क्लास पर MSDN पेज इस पर टिप्पणी नहीं करता है, और "प्रो C # 2010 ..." पुस्तक उसी पैटर्न की सिफारिश करती है और कार्य निपटान पर कोई टिप्पणी नहीं करती है।

मुझे पता है कि अगर मैं इसे छोड़ देता हूं तो फाइनली इसे अंत में पकड़ लिया जाएगा, लेकिन क्या यह वापस आने वाला है और मुझे काटता है जब मैं बहुत आग लगा रहा हूं और इस तरह के कार्यों को भूल जाता हूं और फाइनल थ्रेड अभिभूत हो जाता है?

तो मेरे सवाल हैं:

  • यह फोन नहीं करने के लिए स्वीकार्य है Dispose()पर Taskइस मामले में वर्ग? और यदि हां, तो इसके जोखिम / परिणाम क्यों हैं?
  • क्या कोई दस्तावेज है जो इस पर चर्चा करता है?
  • या क्या उस Taskवस्तु का निपटान करने का एक उपयुक्त तरीका है जिसे मैंने याद किया है?
  • या क्या TPL के साथ आग लगाने और कार्यों को भूलने का एक और तरीका है?

जवाबों:


108

MSDN फ़ोरम में इसके बारे में चर्चा है

Microsoft pfx टीम के सदस्य स्टीफन टूब का यह कहना है:

टास्क डिसपोजल टास्क संभावित रूप से एक इवेंट हैंडल को लपेटने के कारण मौजूद होता है जिसका उपयोग टास्क को पूरा करने के लिए इंतजार करने के दौरान किया जाता है, ऐसी स्थिति में वेटिंग थ्रेड को वास्तव में ब्लॉक करना होता है (जैसा कि उस पर इंतजार कर रहे टास्क को पूरा करने या संभावित रूप से निष्पादित करने का विरोध किया जाता है)। यदि आप जो कुछ भी कर रहे हैं वह निरंतरता का उपयोग कर रहा है, तो उस इवेंट हैंडल को कभी भी आवंटित नहीं किया जाएगा
...
चीजों की देखभाल के लिए अंतिम रूप से भरोसा करना बेहतर होगा।

अपडेट (अक्टूबर 2012)
स्टीफन टूब ने एक ब्लॉग पोस्ट किया है जिसका शीर्षक है क्या मुझे कार्य निपटाने की आवश्यकता है?जो कुछ अधिक विस्तार देता है, और .Net 4.5 में सुधार बताते हैं।

संक्षेप में: आपको निपटाने की आवश्यकता नहीं है Task समय के 99% वस्तुओं ।

किसी वस्तु को निष्क्रिय करने के दो मुख्य कारण हैं: समयबद्ध, निर्धारक तरीके से अप्रबंधित संसाधनों को मुक्त करना, और वस्तु के अंतिम रूप से चलाने की लागत से बचना। इनमें से Taskअधिकांश समय पर लागू नहीं होते हैं :

  1. .Net 4.5 के रूप में, केवल एक बार Taskआंतरिक प्रतीक्षा संभाल ( Taskऑब्जेक्ट में एकमात्र अप्रबंधित संसाधन ) आवंटित करता है, जब आप स्पष्ट रूप से उपयोग करते हैंIAsyncResult.AsyncWaitHandle से Task, और का हैं
  2. Taskवस्तु अपने आप में एक finalizer नहीं है; संभाल ही एक अंतिम रूप से किसी वस्तु में लिपटी है, इसलिए जब तक इसे आवंटित नहीं किया जाता है, चलाने के लिए कोई अंतिम उपकरण नहीं है।

3
धन्यवाद, दिलचस्प। यह MSDN प्रलेखन के खिलाफ जाता है, हालांकि। क्या MS या .net टीम का कोई आधिकारिक शब्द है कि यह स्वीकार्य कोड है। उस चर्चा के अंत में उठाया गया बिंदु यह भी है कि "क्या होगा यदि कार्यान्वयन भविष्य के संस्करण में बदलता है"
साइमन पी स्टीवंस

वास्तव में, मैंने अभी देखा है कि थ्रेड में उत्तर देने वाला वास्तव में microsoft में काम करता है, प्रतीत होता है कि pfx टीम पर है, इसलिए मुझे लगता है कि यह एक आधिकारिक उत्तर है। लेकिन सभी मामलों में काम न करने की दिशा में सुझाव है। अगर कोई संभावित रिसाव है तो क्या मैं थ्रेडपूल.क्यूएयूयूसेवरवर्क इट रिवरमेंट से बेहतर हूं जो मुझे पता है कि सुरक्षित है?
साइमन पी स्टीवंस ने

हां, यह बहुत अजीब है कि एक ऐसा डिस्पोज है जिसे आप नहीं कह सकते। यदि आप यहाँ नमूने देखें तो msdn.microsoft.com/en-us/library/dd537610.aspx और यहाँ msdn.microsoft.com/en-us/library/dd537609.aspx वे कार्यों का निपटान कर रहे हैं। हालांकि MSDN में कोड नमूने कभी-कभी बहुत खराब तकनीकों को प्रदर्शित करते हैं। साथ ही उस व्यक्ति ने प्रश्न पर उत्तर दिया जो Microsoft के लिए काम करता है।
किरिल मुजकोव

2
@Simon: (1) MSDN डॉक आपके द्वारा बोली गई जेनेरिक सलाह है, विशिष्ट मामलों में अधिक विशिष्ट सलाह है (जैसे कि UI थ्रेड पर कोड चलाने के लिए EndInvokeविनफॉर्म में उपयोग करने की आवश्यकता नहीं है BeginInvoke)। (2) स्टीफन टूब पीएफएक्स के प्रभावी उपयोग (जैसे channel9.msdn.com ) पर एक नियमित वक्ता के रूप में अच्छी तरह से जानते हैं , इसलिए यदि कोई अच्छा मार्गदर्शन दे सकता है तो वह यही है। अपने दूसरे पैराग्राफ पर ध्यान दें: कई बार चीजों को अंतिम रूप देने के लिए बेहतर होता है।
रिचर्ड

12

यह थ्रेड क्लास के साथ उसी तरह का मुद्दा है। यह 5 ऑपरेटिंग सिस्टम हैंडल्स का उपभोग करता है, लेकिन आईडीआईसपोजेबल लागू नहीं करता है। मूल डिजाइनरों का अच्छा निर्णय, डिस्पोज़ () पद्धति को कॉल करने के कुछ निश्चित तरीके हैं। आपको पहले Join () कॉल करना होगा।

टास्क क्लास इसमें एक हैंडल जोड़ता है, एक आंतरिक मैनुअल रीसेट इवेंट। सबसे सस्ता ऑपरेटिंग सिस्टम संसाधन कौन सा है। बेशक, इसकी डिस्पोज़ () विधि केवल उस ईवेंट हैंडल को जारी कर सकती है, न कि 5 हैंडल जो थ्रेड खपत करते हैं। हाँ, परेशान मत करो

इस बात से सावधान रहें कि आपको इस कार्य के लिए आवश्यक संपत्ति में दिलचस्पी है। यह काफी बदसूरत विषय है, आप इस MSDN लाइब्रेरी लेख में इसके बारे में अधिक पढ़ सकते हैं । एक बार जब आप इससे ठीक से निपट लेते हैं, तो आपको कार्यों को निपटाने के लिए अपने कोड में एक अच्छा स्थान होना चाहिए।


6
लेकिन एक कार्य Threadज्यादातर मामलों में नहीं बनाता है , यह थ्रेडपूल का उपयोग करता है।
svick

-1

मैं इस पोस्ट में दिखाई गई तकनीक पर किसी का वजन देखना पसंद करूंगा: तौलते हुए देखना पसंद करूंगा टाइपफेस्ट फायर एंड-एसिंक्रोनस डेलीगेट इनवॉइस सी #

ऐसा लगता है कि एक साधारण एक्सटेंशन विधि कार्यों के साथ बातचीत के सभी तुच्छ मामलों को संभाल लेगी और उस पर निपटान को कॉल करने में सक्षम होगी।

public static void FireAndForget<T>(this Action<T> act,T arg1)
{
    var tsk = Task.Factory.StartNew( ()=> act(arg1),
                                     TaskCreationOptions.LongRunning);
    tsk.ContinueWith(cnt => cnt.Dispose());
}

3
बेशक, जो Taskवापस लौटाए गए उदाहरण का निपटान करने में विफल रहता है ContinueWith, लेकिन स्टीफन टाउब के उद्धरण को स्वीकार किए गए उत्तर है: यदि किसी कार्य पर अवरुद्ध प्रतीक्षा नहीं करता है, तो निपटान करने के लिए कुछ भी नहीं है।
रिचर्ड

1
जैसा कि रिचर्ड उल्लेख करते हैं, ContinueWith (...) भी एक दूसरी टास्क ऑब्जेक्ट देता है जो तब निपट नहीं पाता है।
साइमन पी स्टीवंस

1
जैसा कि यह बताता है कि कंटिन्यू कोड वास्तव में अतिरेक से भी बदतर है क्योंकि यह पुराने कार्य को निपटाने के लिए एक और कार्य पैदा करेगा। इस तरह से खड़ा होने के साथ यह मूल रूप से इस कोड ब्लॉक में एक अवरुद्ध प्रतीक्षा को शुरू करने के लिए संभव नहीं होगा यदि आप जिस कार्य प्रतिनिधि को इसमें पारित करते हैं वह टास्क को भी हेरफेर करने का प्रयास कर रहा था?
क्रिस मैरिकिक

1
आप उपयोग करने में सक्षम हो सकते हैं कि दूसरे काम की देखभाल के लिए लैम्ब्डा कैसे थोड़े मुश्किल तरीके से चर पर कब्जा करते हैं। Task disper = null; disper = tsk.ContinueWith(cnt => { cnt.Dispose(); disper.Dispose(); });
गिदोन एंगेलबर्ट

@GideonEngelberth प्रतीत होता है कि काम करना होगा। चूंकि जीसी द्वारा डिस्पेर को कभी भी निपटाया नहीं जाना चाहिए, यह तब तक वैध रहना चाहिए जब तक कि लंबोदर खुद को निपटाने के लिए आमंत्रित नहीं करता, यह मानते हुए कि संदर्भ अभी भी वहां मान्य है / श्रग। शायद इसके आसपास एक खाली कोशिश / पकड़ने की ज़रूरत है?
क्रिस मैरिसिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.