सभी सर्वर-साइड कोड के लिए कॉन्फ़िगर कॉन्फ़िगर करने के लिए सबसे अच्छा अभ्यास


561

जब आपके पास सर्वर-साइड कोड (यानी कुछ ApiController) और आपके फ़ंक्शन अतुल्यकालिक हैं - तो वे वापस लौटते हैं Task<SomeObject>- क्या यह सबसे अच्छा अभ्यास माना जाता है कि किसी भी समय आप उन कार्यों का इंतजार करते हैं जिन्हें आप कॉल करते हैं ConfigureAwait(false)?

मैंने पढ़ा था कि यह अधिक अच्छा है क्योंकि इसे मूल संदर्भ के लिए थ्रेड संदर्भों को वापस स्विच करने की आवश्यकता नहीं है। हालाँकि, ASP.NET वेब एप के साथ, यदि आपका अनुरोध एक थ्रेड पर आ रहा है, और आप कुछ फ़ंक्शन का इंतजार करते हैं और कॉल करते हैं ConfigureAwait(false)जो संभावित रूप से आपको एक अलग थ्रेड पर रख सकता है जब आप अपने ApiControllerफ़ंक्शन का अंतिम परिणाम दे रहे हैं ।

मैंने नीचे किस बारे में बात कर रहा हूं उसका एक उदाहरण टाइप किया है:

public class CustomerController : ApiController
{
    public async Task<Customer> Get(int id)
    {
        // you are on a particular thread here
        var customer = await SomeAsyncFunctionThatGetsCustomer(id).ConfigureAwait(false);

        // now you are on a different thread!  will that cause problems?
        return customer;
    }
}

जवाबों:


627

अद्यतन: ASP.NET Core में a नहीं हैSynchronizationContext । यदि आप ASP.NET Core पर हैं, तो इससे कोई फर्क नहीं पड़ता कि आप उपयोग करते हैं ConfigureAwait(false)या नहीं।

ASP.NET "पूर्ण" या "क्लासिक" या जो भी हो, इस उत्तर के बाकी अभी भी लागू होते हैं।

मूल पोस्ट (नॉन-कोर ASP.NET के लिए):

ASP.NET टीम के इस वीडियो में ASP.NET का उपयोग asyncकरने की सबसे अच्छी जानकारी है ।

मैंने पढ़ा था कि यह अधिक अच्छा है क्योंकि इसे मूल संदर्भ के लिए थ्रेड संदर्भों को वापस स्विच करने की आवश्यकता नहीं है।

यह यूआई अनुप्रयोगों के साथ सच है, जहां केवल एक यूआई धागा है जिसे आपको वापस "सिंक" करना है।

ASP.NET में, स्थिति थोड़ी अधिक जटिल है। जब कोई asyncविधि निष्पादन शुरू करती है, तो वह ASP.NET थ्रेड पूल से एक थ्रेड पकड़ती है। यदि आप संदर्भ कैप्चर का उपयोग करके अक्षम करते हैं ConfigureAwait(false), तो थ्रेड सीधे विधि को निष्पादित करना जारी रखता है। यदि आप संदर्भ कैप्चर को अक्षम नहीं करते हैं, तो धागा अनुरोध के संदर्भ में फिर से प्रवेश करेगा और फिर विधि को निष्पादित करना जारी रखेगा।

तो ConfigureAwait(false)ASP.NET में आपको थ्रेड जंप नहीं बचाता है; यह आपको अनुरोध के संदर्भ में फिर से प्रवेश करने से बचाता है, लेकिन यह सामान्य रूप से बहुत तेज है। यदि आप एक अनुरोध के समानांतर प्रसंस्करण की एक छोटी राशि करने की कोशिश कर रहे हैं, तो उपयोगी ConfigureAwait(false) हो सकता है, लेकिन वास्तव में TPL उन परिदृश्यों के लिए एक बेहतर फिट है।

हालाँकि, ASP.NET वेब एपी के साथ, यदि आपका अनुरोध एक थ्रेड पर आ रहा है, और आप कुछ फ़ंक्शन का इंतजार करते हैं और ConfigureAwait (गलत) कॉल करते हैं, जो संभावित रूप से आपको एक अलग थ्रेड पर डाल सकता है जब आप अपने ApiChroller फ़ंक्शन का अंतिम परिणाम लौटा रहे हैं। ।

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

ConfigureAwaitASP.NET में एकमात्र अंतर यह है कि क्या विधि को फिर से शुरू करते समय थ्रेड अनुरोध संदर्भ में प्रवेश करता है।

मेरे और मेरे परिचय ब्लॉग पोस्ट परSynchronizationContext मेरे MSDN लेख में पृष्ठभूमि की अधिक जानकारी है ।async


23
थ्रेड-स्थानीय भंडारण किसी भी संदर्भ से प्रवाहित नहीं होता है । HttpContext.CurrentASP.NET द्वारा प्रवाहित किया जाता है SynchronizationContext, जिसे डिफ़ॉल्ट रूप से आपके द्वारा प्रवाहित किया जाता है await, लेकिन यह इसके द्वारा प्रवाहित नहीं होता है ContinueWith। OTOH, निष्पादन संदर्भ (सुरक्षा प्रतिबंधों सहित) संदर्भ के माध्यम से सी # CLR में उल्लेख किया है, और यह है दोनों से प्रवाहित होती ContinueWithहै और await(भले ही आप का उपयोग ConfigureAwait(false))।
स्टीफन क्लीयर

65
क्या यह बहुत अच्छा नहीं होगा अगर C # को ConfigureAwait (गलत) के लिए मूल भाषा समर्थन था? कुछ ऐसा है जैसे awa वाट्सएप ’(इंतजार का कोई संदर्भ नहीं)। हर जगह एक अलग विधि कॉल टाइपिंग बहुत कष्टप्रद है। :)
नाथनल्डनश्री

19
@NathanAldenSr: इसकी काफी चर्चा हुई थी। नए कीवर्ड के साथ समस्या यह है कि ConfigureAwaitवास्तव में केवल तभी समझ में आता है जब आप कार्यों का इंतजार करते हैं , जबकि awaitकिसी भी " प्रतीक्षा योग्य " पर कार्य करता है। अन्य विकल्पों पर विचार किया गया: क्या लाइब्रेरी में डिफ़ॉल्ट व्यवहार को संदर्भ को छोड़ देना चाहिए? या डिफ़ॉल्ट संदर्भ व्यवहार के लिए एक संकलक सेटिंग है? इन दोनों को अस्वीकार कर दिया गया था क्योंकि यह सिर्फ कोड को पढ़ना और यह बताता है कि यह क्या करना मुश्किल है।
स्टीफन क्लीयर

10
@AnshulNigam: यही कारण है कि नियंत्रक कार्यों को उनके संदर्भ की आवश्यकता है। लेकिन अधिकांश विधियाँ जिन्हें क्रियाएँ कहते हैं, नहीं करती हैं।
स्टीफन क्लीयर

14
@JonathanRoeder: सामान्य शब्दों में, आप की जरूरत नहीं होनी चाहिए ConfigureAwait(false)एक से बचने के लिए Result/ Waitआधारित गतिरोध क्योंकि ASP.NET पर आप नहीं का उपयोग करना चाहिए Result/ Waitपहली जगह में।
स्टीफन क्लीयर

131

आपके प्रश्न का संक्षिप्त उत्तर: नहीं। आपको ConfigureAwait(false)उस तरह के आवेदन स्तर पर नहीं बुलाना चाहिए ।

टीएल; लंबे उत्तर का डीआर संस्करण: यदि आप एक पुस्तकालय लिख रहे हैं, जहां आप अपने उपभोक्ता को नहीं जानते हैं और आपको एक सिंक्रनाइज़ेशन संदर्भ की आवश्यकता नहीं है (जो आपको विश्वास नहीं है कि पुस्तकालय में होना चाहिए), तो आपको हमेशा उपयोग करना चाहिए ConfigureAwait(false)। अन्यथा, आपके पुस्तकालय के उपभोक्ता अवरुद्ध शैली में आपके अतुल्यकालिक तरीकों का उपभोग करके गतिरोध का सामना कर सकते हैं। यह स्थिति पर निर्भर करता है।

यहाँ ConfigureAwaitविधि के महत्व पर थोड़ा और विस्तृत विवरण दिया गया है (मेरे ब्लॉग पोस्ट का एक उद्धरण):

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

एक विधि का उपभोग करते समय, जो नई एसिंक्रोनस भाषा की विशेषताओं का उपयोग करता है, एक अवरुद्ध फैशन में, यदि आपके पास उपलब्ध सिंक्रोनाइज़ेशनकोटेक्स्ट है, तो आप एक गतिरोध के साथ समाप्त हो जाएंगे। जब आप एक अवरुद्ध फैशन में ऐसी विधियों का उपभोग कर रहे हैं (प्रतीक्षा पद्धति के साथ कार्य पर प्रतीक्षा कर रहे हैं या सीधे परिणाम टास्क की संपत्ति से ले रहे हैं), तो आप एक ही समय में मुख्य थ्रेड को ब्लॉक कर देंगे। जब अंततः टास्क थ्रेडपूल में उस विधि के अंदर पूरा हो जाता है, तो यह मुख्य धागे पर वापस पोस्ट करने के लिए निरंतरता को लागू करने वाला है क्योंकि SynchronizationContext.Currentयह उपलब्ध है और कब्जा कर लिया गया है। लेकिन यहां एक समस्या है: यूआई धागा अवरुद्ध है और आपके पास गतिरोध है!

इसके अलावा, यहां आपके लिए दो बेहतरीन लेख हैं जो आपके प्रश्न के लिए सटीक हैं:

अंत में, इस विषय पर वास्तव में लुसियन विस्किक से एक महान लघु वीडियो है : Async पुस्तकालय विधियों में टास्क का उपयोग करने पर विचार करना चाहिएConfigureAwait (गलत)

उम्मीद है की यह मदद करेगा।


2
"टास्क का गेटअवेटर तरीका सिंक्रोनाइज़ेशनकोटेक्स्ट.क्रंटल के लिए दिखता है। यदि वर्तमान सिंक्रनाइज़ेशन संदर्भ शून्य नहीं है, तो उस वेटर के पास जाने वाली निरंतरता उस सिंक्रनाइज़ेशन संदर्भ में वापस पोस्ट हो जाएगी।" - मुझे यह आभास हो रहा है कि आप कहने की कोशिश कर रहे हैं कि Taskस्टैक को चलना है SynchronizationContext, जो गलत है। SynchronizationContextकरने के लिए कॉल करने से पहले पकड़ा जाता है Taskऔर उसके बाद कोड के बाकी पर जारी है SynchronizationContextअगर SynchronizationContext.Currentअशक्त नहीं है।
कैस्पर ओने

1
@casperOne मैंने ऐसा ही कहने का इरादा किया है।
tugberk

8
क्या यह सुनिश्चित करने के लिए फोन करने वाले की ज़िम्मेदारी नहीं होनी चाहिए कि SynchronizationContext.Currentयह स्पष्ट है / या कि लाइब्रेरी को क्लास लाइब्रेरी में Task.Run()लिखने के बजाय कहा जाता है .ConfigureAwait(false)?
बिंकी

1
@binki - दूसरी ओर: (1) संभवतः एक पुस्तकालय का उपयोग कई अनुप्रयोगों में किया जाता है, इसलिए अनुप्रयोगों को आसान बनाने के लिए पुस्तकालय में एक बार प्रयास करना लागत प्रभावी है; (२) संभवतः पुस्तकालय लेखक जानता है कि उसने ऐसा कोड लिखा है जिसके मूल संदर्भ को जारी रखने की आवश्यकता नहीं है, जिसे वह उन .ConfigureAwait(false)एस के साथ व्यक्त करता है । शायद लाइब्रेरी के लेखकों के लिए यह आसान होगा यदि वह डिफ़ॉल्ट व्यवहार था, लेकिन मैं यह मानूंगा कि लाइब्रेरी को सही ढंग से लिखना थोड़ा कठिन है और ऐप को सही तरीके से लिखना थोड़ा कठिन है।
टूलमेकरसेव

4
एक पुस्तकालय के लेखक को उपभोक्ता को क्यों कोडित करना चाहिए? यदि उपभोक्ता गतिरोध करना चाहता है, तो मुझे उन्हें क्यों रोकना चाहिए?
क्वार्कली

25

ConfigureAwait (गलत) का उपयोग करके मैंने जो सबसे बड़ी ड्रा बैक पाया है, वह यह है कि थ्रेड कल्चर को सिस्टम डिफॉल्ट में वापस कर दिया गया है। यदि आपने एक संस्कृति को कॉन्फ़िगर किया है जैसे ...

<system.web>
    <globalization culture="en-AU" uiCulture="en-AU" />    
    ...

और आप ऐसे सर्वर पर होस्ट कर रहे हैं, जिसकी संस्कृति en-US पर सेट है, तो आप ConfigureAwait (गलत) से पहले पाएंगे कि उसे CultureInfo.CurrentCulture एन-एयू लौटाएगा और आपके पास एन-यूएस मिलेगा। अर्थात

// CultureInfo.CurrentCulture ~ {en-AU}
await xxxx.ConfigureAwait(false);
// CultureInfo.CurrentCulture ~ {en-US}

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


27
.NET के आधुनिक संस्करण (मुझे लगता है कि 4.6 के बाद से?) धागे के पार संस्कृति का प्रचार करेंगे, भले ही ConfigureAwait(false)इसका उपयोग किया जाए।
स्टीफन क्लीयर

1
जानकारी के लिए धन्यवाद। हम वास्तव में .net 4.5.2 का उपयोग कर रहे हैं
मिक

11

मेरे कार्यान्वयन के बारे में कुछ सामान्य विचार हैं Task:

  1. टास्क डिस्पोजेबल है फिर भी हम उपयोग करने वाले नहीं हैं using
  2. ConfigureAwait4.5 में पेश किया गया था। Task4.0 में पेश किया गया था।
  3. .NET थ्रेड्स हमेशा संदर्भ को प्रवाहित करते हैं (CR पुस्तक के माध्यम से C # देखें), लेकिन डिफ़ॉल्ट कार्यान्वयन में Task.ContinueWithवे b / c नहीं थे यह एहसास हुआ कि संदर्भ स्विच महंगा है और इसे डिफ़ॉल्ट रूप से बंद कर दिया गया है।
  4. समस्या यह है कि एक पुस्तकालय डेवलपर को यह ध्यान नहीं रखना चाहिए कि उसके ग्राहकों को संदर्भ प्रवाह की आवश्यकता है या नहीं, इसलिए यह तय नहीं करना चाहिए कि संदर्भ प्रवाह है या नहीं।
  5. [बाद में जोड़ा गया] यह तथ्य कि कोई आधिकारिक जवाब और उचित संदर्भ नहीं है और हम इस बात पर लड़ते रहते हैं कि किसी ने अपना काम सही नहीं किया है।

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

समस्या तब होती है जब एक पुस्तकालय एक तुल्यकालिक एपीआई को उजागर करता है लेकिन एक अन्य अतुल्यकालिक एपीआई का उपयोग करता है - इसलिए आपको अपने कोड में उपयोग Wait()/ उपयोग करने की आवश्यकता है Result


6
1) आप चाहें Task.Disposeतो कॉल कर सकते हैं; आप बस समय के विशाल बहुमत की जरूरत नहीं है। 2) TaskTPL के भाग के रूप में .NET 4.0 में पेश किया गया था, जिसकी आवश्यकता नहीं थी ConfigureAwait; जब asyncजोड़ा गया था, तो उन्होंने Taskएक नया आविष्कार करने के बजाय मौजूदा प्रकार का पुन: उपयोग किया Future
स्टीफन क्लीयर

6
3) आप दो अलग-अलग प्रकार के "संदर्भ" को भ्रमित कर रहे हैं। सीएलआर के माध्यम से सी # में उल्लिखित "संदर्भ" हमेशा प्रवाहित होता है, यहां तक ​​कि Taskएस में भी ; "संदर्भ" द्वारा नियंत्रित ContinueWithएक है SynchronizationContextया TaskSchedulerस्टीफन टूब के ब्लॉग पर इन विभिन्न संदर्भों के बारे में विस्तार से बताया गया है
स्टीफन क्लीयर

21
4) पुस्तकालय लेखक को यह ध्यान रखने की आवश्यकता नहीं है कि उसके कॉलर्स को संदर्भ प्रवाह की आवश्यकता है, क्योंकि प्रत्येक अतुल्यकालिक विधि स्वतंत्र रूप से फिर से शुरू होती है। इसलिए यदि कॉलर्स को संदर्भ प्रवाह की आवश्यकता होती है, तो वे इसे प्रवाहित कर सकते हैं, भले ही पुस्तकालय लेखक ने इसे प्रवाहित किया हो या नहीं।
स्टीफन क्लीयर

1
सबसे पहले, आप प्रश्न का उत्तर देने के बजाय शिकायत करते दिखते हैं। और फिर आप "संदर्भ" के बारे में बात कर रहे हैं, सिवाय इसके कि कई प्रकार के संदर्भ हैं। नेट और यह वास्तव में स्पष्ट नहीं है कि आप (या लोगों के) किसके बारे में बात कर रहे हैं? और यहां तक ​​कि अगर आप अपने आप को भ्रमित नहीं कर रहे हैं (लेकिन मुझे लगता है कि आप हैं, मेरा मानना ​​है कि ऐसा कोई संदर्भ नहीं है जो Threadएस के साथ प्रवाह करता था, लेकिन अब और नहीं के साथ ContinueWith()), यह आपके जवाब को पढ़ने के लिए भ्रमित करता है।
svick

1
@StephenCleary हाँ, काम देव को जानने की जरूरत नहीं है, यह ग्राहक के लिए है। मुझे लगा कि मैंने इसे स्पष्ट कर दिया है, लेकिन मेरा वाक्यांश स्पष्ट नहीं था।
अलीओस्ताद
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.