एक महत्वपूर्ण बात है जब लिए इंतजार का उपयोग कर उपयोग करने के लिए लागू async एपीआई ConfigureAwait(false) उपयोग करने के लिए जब भी आप एपीआई impl अंदर एक काम का इंतजार करना चाहते हैं सुनिश्चित करने के लिए है। ऐसा करने के लिए TPLAwaiter के डिफ़ॉल्ट व्यवहार (वर्तमान सिंक संदर्भ) की बजाय TPL डिफ़ॉल्ट व्यवहार (थ्रेडपूल) का उपयोग करके टीपीएल को आपकी प्रतीक्षा बहाली को शेड्यूल करने की अनुमति है।
वर्तमान सिंक संदर्भ का उपयोग उपभोक्ताओं के लिए सही डिफ़ॉल्ट व्यवहार है क्योंकि यह यूआई थ्रेड पर लौटने की प्रतीक्षा करने जैसी चीजों की अनुमति देता है, अगर आप पहले ही यूआई थ्रेड पर थे। हालांकि, UI थ्रेड पर वापस आने का प्रयास करने में समस्या हो सकती है यदि UI थ्रेड शेष विधि को निष्पादित करने के लिए उपलब्ध नहीं है। जिस तरह से await
विधि को निष्पादित करने के लिए थ्रेड प्राप्त करता है वह हूड के नीचे प्रतिनिधियों को बनाने का मानक .NET सम्मेलन है। तब इन प्रतिनिधियों को किसी भी तरह के प्रेषण तंत्र (जैसे WinForms संदेश पंप, WPF प्रेषक, या कुछ और) में संसाधित करने के लिए भेजा जाता है।
हालांकि, वापस उसी संदर्भ के लिए आने की कोशिश कर आम तौर पर एपीआई क्रियान्वयन के लिए गलत बात है, क्योंकि वह परोक्ष कि मूल संदर्भ निष्पादन के लिए उपलब्ध होने पर निर्भरता लेता है।
उदाहरण के लिए, मैं यूआई धागे पर कुछ कोड है, तो:
void MyUIThreadCode() {
Task asyncTask = MyAsyncMethod();
asyncTask.Wait();
}
async Task MyAsyncMethod() {
await DownloadSomethingAsync();
ComputeSomethingElse();
}
कोड इस तरह की है [ख] बहुत [/ ख] लिखने के लिए आकर्षक है, और रुक जाता है पैदा करने के लिए बहुत ही आसान। सामान्य मामला यह है कि MyAsyncMethod()
के अंदर, एक प्रतीक्षा है जो डिफ़ॉल्ट सिंक संदर्भ शेड्यूलिंग का उपयोग करती है। इसका मतलब है कि यूआई संदर्भ में, DownloadSomethingAsync() विधि कॉल की जाएगी, और डाउनलोड शुरू हो जाएगा।
MyAsyncMethod()
तब परीक्षण करें कि await
ऑपरेंड "किया गया" है। मान लें कि इसे डाउनलोड नहीं किया है करते हैं, तो फिर await
के लिए परिभाषित व्यवहार विधि के "आराम" दूर उत्कीर्ण, और अनुसूची कि निष्पादन के लिए एक बार await
संकार्य वास्तव में किया है के लिए है।
तो ... बाकी विधि को निष्पादित करने के लिए राज्य एक प्रतिनिधि में बंद हो जाता है, और अब MyAsyncMethod()
MyUIThreadCode()
पर अपना कार्य वापस लौटाता है।
अब MyUIThreadCode()
लौटे काम पर Task.Wait()
कहता है। लेकिन मुद्दा यह है कि .NET में Task
वास्तव में किसी भी चीज का एक सामान्य उद्देश्य प्रतिनिधित्व है जिसमें "पूर्ण-निष्पादन" की धारणा है। सिर्फ इसलिए कि आपके पास Task
ऑब्जेक्ट है, यह गारंटी देने के लिए कुछ भी नहीं है कि यह कैसे निष्पादित किया जा रहा है, न ही यह कैसे पूरा होने जा रहा है। यदि आप अनुमान लगा रहे हैं, तो दूसरी चीज जिसकी गारंटी नहीं है, इसकी निहित निर्भरताएं हैं।
तो उपर्युक्त उदाहरण में, MyAsyncMethod()
कार्य पर डिफ़ॉल्ट प्रतीक्षा व्यवहार का उपयोग करता है, जो वर्तमान संदर्भ पर विधि निरंतरता निर्धारित करता है। विधि निरंतरता को MyAsyncMethod()
के लौटाए गए कार्य को पूरा करने से पहले निष्पादित करने की आवश्यकता है।
हालांकि, MyUIThreadCode()
काम पर Wait()
कहा जाता है। परिभाषित व्यवहार ब्लॉक वर्तमान धागा करने के लिए, ढेर पर वर्तमान समारोह रखने के लिए, और प्रभावी ढंग से जब तक कार्य किया जाता है इंतजार है।
क्या उपयोगकर्ता यहां एहसास नहीं था कि काम वे पर ब्लॉक किए गए हैं निर्भर करता है यूआई धागे पर सक्रिय रूप से प्रसंस्करण यह अभी भी समारोह है कि पर अवरुद्ध है क्रियान्वित करने में व्यस्त है जिसकी वजह से यह नहीं कर सकते था Wait()
कॉल करें।
- MyUIThreadCode के लिए आदेश() ने अपने विधि कॉल समाप्त करने के लिए यह
Wait()
के लिए की जरूरत है (2)
- प्रतीक्षा के लिए आदेश में() वापस जाने के लिए वापस जाने के लिए, यह asyncTask की जरूरत है (3) पूरा करने के लिए।
- asyncTask को पूरा करने के लिए, इसे निष्पादित करने के लिए
MyAsyncMethod()
से विधि निरंतरता की आवश्यकता है (4)।
- विधि निरंतरता चलाने के क्रम में, इसे संदेश लूप को संसाधित करने की आवश्यकता है (5)।
- संदेश लूप को प्रसंस्करण जारी रखने के लिए, इसे वापस करने के लिए MyUIThreadCode() की आवश्यकता है (1)।
सर्कुलर निर्भरता वर्तनी है, कोई भी स्थिति संतुष्ट होने के समाप्त नहीं होती है, और प्रभावी रूप से UI थ्रेड लटकती है।
यहाँ कैसे आप इसे ConfigureAwait (गलत) के साथ ठीक है:
void MyUIThreadCode() {
Task asyncTask = MyAsyncMethod();
asyncTask.Wait();
}
async Task MyAsyncMethod() {
await DownloadSomethingAsync().ConfigureAwait(false);
ComputeSomethingElse();
}
यहाँ क्या होता है कि MyAsyncMethod()
के लिए विधि निरंतरता है, TPL डिफ़ॉल्ट (ThreadPool) का उपयोग करता है बल्कि वर्तमान समन्वयन संदर्भ से है। यह Wait()
के लिए की जरूरत है वापस जाने के लिए (2)
प्रतीक्षा के लिए आदेश में() वापस जाने के लिए
- (MyUIThreadCode के लिए आदेश में) ने अपने विधि कॉल समाप्त करने के लिए,,: और यहाँ है कि व्यवहार के साथ अब स्थितियां हैं, इसे पूरा करने के लिए asyncTask की आवश्यकता है (3)।
- asyncTask को पूरा करने के लिए, इसे निष्पादित करने के लिए
MyAsyncMethod()
से विधि निरंतरता की आवश्यकता है (4)।
- (नया) विधि निरंतर चलने के क्रम में, इसे थ्रेड पूल को प्रसंस्करण (5) की आवश्यकता होती है।
- प्रभावी रूप से, थ्रेडपूल हमेशा प्रसंस्करण होता है। वास्तव में, .NET थ्रेडपूल को उच्च स्केलेबिलिटी (गतिशील रूप से आवंटित और थ्रेड्स सेवानिवृत्त) के साथ बहुत अधिक ट्यून किया गया है, साथ ही कम विलंबता (इसमें अधिकतम सीमा है कि यह आगे बढ़ने से पहले, इससे पहले कि यह आगे बढ़ने से पहले, थ्रूपुट सुनिश्चित करने के लिए एक नया धागा संरक्षित है)।
आप .NET पर पहले से ही एक ठोस मंच होने पर शर्त लगा रहे हैं, और हम थ्रेड पूल को .NET में बहुत गंभीरता से लेते हैं।
तो, कोई शायद पूछ सकता है कि समस्या Wait()
कॉल के साथ है ... उन्होंने शुरू करने के लिए अवरुद्ध प्रतीक्षा का उपयोग क्यों किया?
उत्तर यह है कि कभी-कभी आपको वास्तव में करना पड़ता है। उदाहरण के लिए, मुख्य() विधियों के लिए .NET अनुबंध यह है कि मुख्य() विधि लौटाते समय प्रोग्राम समाप्त हो जाता है। या ... दूसरे शब्दों में, मुख्य() विधि आपके प्रोग्राम किए जाने तक ब्लॉक करता है।
इंटरफ़ेस अनुबंधों या वर्चुअल विधि अनुबंधों जैसी अन्य चीजें आमतौर पर विशिष्ट वादे करती हैं कि कुछ विधि उस विधि से पहले की जाती हैं। जब तक आपका इंटरफ़ेस या आभासी विधि कोई कार्य वापस न दे ... आपको किसी भी एसिंक एपीआई पर कुछ अवरुद्ध करने की आवश्यकता होगी। यह प्रभावी रूप से उस स्थिति में एसिंक के उद्देश्य को हरा देता है ... लेकिन शायद आपको एक अलग कोडपैथ में एसिंक्रोन से लाभ होता है।
इस प्रकार एपीआई प्रदाताओं के लिए जो कॉन्फ़िगरएवाइट (झूठी) का उपयोग करके एसिंक कार्य वापस करते हैं, आप यह सुनिश्चित करने में सहायता कर रहे हैं कि आपके लौटे हुए कार्यों में कोई अप्रत्याशित अंतर्निहित निर्भरता नहीं है (जैसे यूआई संदेश लूप अभी भी सक्रिय रूप से पंपिंग कर रहा है)। जितना अधिक आप अपनी निर्भरताओं को जोड़ सकते हैं, उतना ही बेहतर एपीआई आप कर सकते हैं।
आशा है कि इससे मदद मिलती है!
यह प्रश्न "रचनात्मक नहीं" क्यों बंद हुआ? यह एक उपयोगी सवाल है, और मैं जवाब जानना चाहता हूं ... –