शून्य को लौटाने और टास्क को वापस करने में क्या अंतर है?


128

विभिन्न C # Async CTP नमूनों को देखने में मुझे कुछ async कार्य दिखाई देते हैं जो वापस आते हैं void, और अन्य जो गैर-जेनेरिक वापस आते हैं Task। मैं देख सकता हूं कि Task<MyType>async ऑपरेशन पूरा होने पर कॉलर को डेटा वापस करने के लिए क्यों लौटना उपयोगी है, लेकिन मैंने जो फ़ंक्शन देखे हैं उनमें रिटर्न प्रकार Taskकभी भी कोई डेटा वापस नहीं करता है। क्यों नहीं लौटा void?

जवाबों:


214

SLaks और किल्करम के उत्तर अच्छे हैं; मैंने सोचा था कि मैं अभी थोड़ा और संदर्भ जोड़ूंगा।

आपका पहला सवाल अनिवार्य रूप से है कि किन तरीकों को चिह्नित किया जा सकता है async

एक विधि के रूप में चिह्नित asyncवापसी कर सकते हैं void, Taskया Task<T>। उनके बीच क्या अंतर हैं?

एक Task<T>लौटते हुए एस्किंट विधि की प्रतीक्षा की जा सकती है, और जब यह कार्य पूरा हो जाएगा तो यह एक टी को सुरक्षित करेगा।

एक Taskलौटते हुए async विधि की प्रतीक्षा की जा सकती है, और जब कार्य पूरा हो जाता है, तो कार्य की निरंतरता को चलाने के लिए निर्धारित किया जाता है।

void लौटाने वाली एस्किंट विधि का इंतजार नहीं किया जा सकता है; यह एक "आग और भूल" विधि है। यह एसिंक्रोनस रूप से काम करता है, और आपके पास यह बताने का कोई तरीका नहीं है कि यह कब किया जाता है। यह थोड़ा अजीब से अधिक है; जैसा कि SLaks कहते हैं, आम तौर पर आप केवल एक अतुल्यकालिक ईवेंट हैंडलर बनाते समय करते हैं। घटना आग, हैंडलर निष्पादित; कोई भी इवेंट हैंडलर द्वारा दिए गए कार्य का "इंतजार" करने वाला नहीं है क्योंकि इवेंट हैंडलर कार्यों को वापस नहीं करता है, और यहां तक ​​कि अगर उन्होंने किया, तो कोड किस चीज के लिए टास्क का उपयोग करेगा? यह आमतौर पर उपयोगकर्ता कोड नहीं है जो पहली बार में हैंडलर को नियंत्रण स्थानांतरित करता है।

आपका दूसरा सवाल, एक टिप्पणी में, अनिवार्य रूप से awaitएड के बारे में हो सकता है :

awaitएड किस प्रकार के तरीके हो सकते हैं ? क्या शून्य-वापसी विधि awaitएड हो सकती है ?

नहीं, शून्य-वापसी विधि की प्रतीक्षा नहीं की जा सकती। संकलक await M()एक कॉल में अनुवाद करता है M().GetAwaiter(), जहां GetAwaiterएक उदाहरण विधि या एक विस्तार विधि हो सकती है। प्रतीक्षित मान एक होना चाहिए जिसके लिए आपको एक वेटर मिल सकता है; स्पष्ट रूप से एक शून्य-वापसी विधि एक मूल्य उत्पन्न नहीं करती है जिससे आप एक वेटर प्राप्त कर सकते हैं।

Task-पूर्ण तरीकों से अचेतन मूल्यों का उत्पादन किया जा सकता है। हम अनुमान लगाते हैं कि तीसरे पक्ष अपनी Task-जैसी वस्तुओं को लागू करना चाहते हैं , जिनकी प्रतीक्षा की जा सकती है, और आप उनकी प्रतीक्षा कर पाएंगे। हालांकि, आपको उन asyncतरीकों को घोषित करने की अनुमति नहीं दी जाएगी जो कुछ भी वापस करते हैं void, लेकिन , Taskया Task<T>

(अद्यतन: मेरे अंतिम वाक्य को C # के भविष्य के संस्करण द्वारा गलत ठहराया जा सकता है; इसमें async विधियों के लिए कार्य प्रकारों के अलावा अन्य रिटर्न प्रकारों को अनुमति देने का प्रस्ताव है।)

(अद्यतन: ऊपर वर्णित सुविधा ने इसे C # 7. में बनाया)


7
+1 मुझे लगता है कि केवल एक चीज गायब है, यह अंतर है कि अपवादों को शून्य-रिटर्न वाले async विधियों में कैसे व्यवहार किया जाता है।
जोहो एंजेलो

10
@JamesCadd: मान लीजिए कुछ अतुल्यकालिक काम एक अपवाद फेंकता है। इसे कौन पकड़ता है? असिंक्रोनस कार्य शुरू करने वाला कोड अब स्टैक पर नहीं है - यह एक ही धागे पर भी नहीं हो सकता है - और अपवाद यह मानते हैं कि स्टैक पर सभी कैच / अंततः ब्लॉक हैं । तो तुम क्या करते हो? हम टास्क में अपवाद जानकारी संग्रहीत करते हैं, ताकि आप बाद में इसका निरीक्षण कर सकें। लेकिन अगर विधि शून्य है तो उपयोगकर्ता कोड के लिए कोई कार्य उपलब्ध नहीं है। उस स्थिति से हम वास्तव में कैसे निपटते हैं यह कुछ विवाद का विषय रहा है और मुझे इस समय याद नहीं है कि हमने क्या निर्णय लिया था।
एरिक लिपर्ट

8
मैंने वास्तव में BUILD पर स्टीफन टूब का यह प्रश्न पूछा था। .NET 4.0 में, बिना किसी अपवाद के, कार्य में अंततः TPL का पता लगाने के बाद वे प्रक्रिया को क्रैश कर देंगे। ४.५ में उन्होंने डिफ़ॉल्ट व्यवहार को बदल दिया है, ताकि बिना अपवाद वाले अपवाद अभी भी टास्कस्क्रिडर के माध्यम से रिपोर्ट किए जाएंगे :: UnobservedTaskException इवेंट, लेकिन अब प्रक्रिया को क्रैश नहीं करेगा। यदि आप पुराना 4.0 व्यवहार चाहते हैं , तो आप <runtime> <ThrowUnobservedTaskException enable = "true" /> </ runtime> के साथ वापस आ सकते हैं। सबसे अधिक संभावना यह है कि बदलाव को फायर-एंड-भूल के समर्थन के लिए ठीक किया गया था।
ड्रू मार्श

4
async voidSynchronizationContextजिस समय उन्होंने निष्पादित करना शुरू किया था, उस समय उनके तरीके सक्रिय थे। यह (तुल्यकालिक) ईवेंट हैंडलर के व्यवहार के समान है। @DrewMarsh: UnobservedTaskExceptionऔर रनटाइम सेटिंग केवल async टास्क विधियों "आग और भूल" पर लागू होती है , विधियों में नहीं async void
स्टीफन क्ली

1
Async अपवाद से निपटने के लिए उद्धरण लिंक जानकारी: blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx#11
ल्यूक पुप्लेट

23

मामले में कॉलर कार्य पर इंतजार करना चाहता है या एक निरंतरता जोड़ना चाहता है।

वास्तव में, लौटने voidका एकमात्र कारण यह है कि अगर आप वापस नहीं लौट सकते हैं Taskक्योंकि आप एक इवेंट हैंडलर लिख रहे हैं।


मैंने सोचा था कि एक शून्य प्रकार को वापस करने के तरीकों का इंतजार करना संभव है - क्या आप थोड़ा विस्तार कर सकते हैं?
जेम्स कैड

1
नहीं, आप नहीं कर सकते। यदि विधि वापस आती है void, तो आपके पास उस कार्य को प्राप्त करने का कोई तरीका नहीं है जो इसे उत्पन्न करता है। (वास्तव में, मुझे यकीन नहीं है कि यह Taskसब पर भी उत्पन्न होता है )
SLaks

18

लौटने के तरीके Taskऔर Task<T>रचना योग्य हैं - जिसका अर्थ है कि आप awaitउन्हें एक के अंदर कर सकते हैंasync विधि के ।

asyncलौटाने के तरीके voidसंयत नहीं हैं, लेकिन उनके पास दो अन्य महत्वपूर्ण गुण हैं:

  1. उन्हें इवेंट हैंडलर के रूप में इस्तेमाल किया जा सकता है।
  2. वे एक "शीर्ष-स्तर" अतुल्यकालिक ऑपरेशन का प्रतिनिधित्व करते हैं।

दूसरा बिंदु महत्वपूर्ण है जब आप एक संदर्भ के साथ काम कर रहे हैं जो एक गिनती बनाए रखता है जो बकाया अतुल्यकालिक संचालन की ।

ASP.NET संदर्भ एक ऐसा संदर्भ है; यदि आप async Taskविधियों का उपयोग करते हैं, तो उन्हें async से प्रतीक्षा किए बिनाvoid विधि , तो ASP.NET अनुरोध बहुत जल्दी पूरा हो जाएगा।

एक अन्य संदर्भ AsyncContextमैंने यूनिट परीक्षण के लिए लिखा है ( यहां उपलब्ध है ) - यह AsyncContext.Runविधि बकाया ऑपरेशन गणना और शून्य होने पर वापस आती है।


12

प्रकार Task<T>टास्क पैरेलल लाइब्रेरी (TPL) का वर्कहॉर्स प्रकार है, यह "कुछ कार्य / नौकरी की अवधारणा का प्रतिनिधित्व करता Tहै जो भविष्य में प्रकार का परिणाम उत्पन्न करने वाला है "। "कार्य जो भविष्य में पूरा होगा लेकिन कोई परिणाम नहीं देता है" की अवधारणा को गैर-सामान्य टास्क प्रकार द्वारा दर्शाया गया है।

सटीक रूप से प्रकार Tका परिणाम कैसे उत्पन्न होने वाला है और किसी विशेष कार्य का कार्यान्वयन विवरण है; कार्य को स्थानीय मशीन पर एक अन्य प्रक्रिया के लिए, दूसरे धागे आदि के लिए तैयार किया जा सकता है। TPL कार्यों को आम तौर पर वर्तमान प्रक्रिया में एक थ्रेड पूल से श्रमिक थ्रेड के लिए तैयार किया जाता है, लेकिन यह कार्यान्वयन विस्तार Task<T>प्रकार के लिए मौलिक नहीं है ; बल्कि a Task<T>किसी भी उच्च-विलंबता ऑपरेशन का प्रतिनिधित्व कर सकता है जो a T

ऊपर अपनी टिप्पणी के आधार पर:

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


संपादित करें: मुझे अक्टूबर 2011 में MSDN मैगज़ीन में एरिक लिपर्ट के लेख का हवाला देना चाहिए क्योंकि पहली बार में इस सामान को समझने में मेरे लिए यह एक बड़ी मदद थी।

अधिक जानकारी के लिए लोडिंग और व्हाइटपेज यहां देखें ।

मैं उम्मीद करता हूँ की यह कुछ मदद के लायक है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.