Ync async ’और when wait’ का उपयोग कैसे और कब करना है


1065

मुख्य बातों की मेरी समझ है कि एक से asyncऔरawait लेकिन उपयोग कर रहा है उन्हें पृष्ठभूमि धागे को उत्पन्न करने के लंबी अवधि के तर्क प्रदर्शन करने के बराबर - करना आसान लिखने के लिए कोड बनाने के लिए और पढ़ने के लिए है?

मैं वर्तमान में सबसे बुनियादी उदाहरण का प्रयास कर रहा हूं। मैंने कुछ टिप्पणियाँ इनलाइन जोड़ी हैं। क्या आप इसे मेरे लिए स्पष्ट कर सकते हैं?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}

48
इसके अलावा, आपके उदाहरण में ध्यान दें कि ऊपर दिए गए कोड को संकलित करने पर आपको एक चेतावनी मिलती है। चेतावनी पर ध्यान दें । यह आपको बता रहा है कि इस कोड का कोई मतलब नहीं है।
एरिक लिपपार्ट

जवाबों:


759

जब उपयोग asyncऔर awaitसंकलक पृष्ठभूमि में एक राज्य मशीन उत्पन्न करता है।

यहाँ एक उदाहरण है जिस पर मुझे आशा है कि मैं कुछ उच्च-स्तरीय विवरणों की व्याख्या कर सकता हूं जो कि चल रहे हैं:

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

ठीक है, तो यहाँ क्या होता है:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); क्रियान्वित होने लगता है LongRunningOperation

  2. स्वतंत्र कार्य मुख्य थ्रेड (थ्रेड ID = 1) मानकर किया जाता है, तब await longRunningTaskतक पहुँच जाता है।

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

एक दूसरा मामला यह होगा कि longRunningTaskपहले ही इसका निष्पादन समाप्त हो चुका है और परिणाम उपलब्ध है। जब await longRunningTaskहमारे पास पहुंचने का परिणाम पहले से ही होता है तो कोड बहुत ही थ्रेड पर निष्पादित करना जारी रखेगा। (इस मामले में प्रिंटिंग परिणाम कंसोल के लिए)। बेशक यह उपरोक्त उदाहरण के लिए मामला नहीं है, जहां इसमें Task.Delay(1000)शामिल है।


65
हमारे पास "टास्क.लेय (1000) के साथ" प्रतीक्षा "क्यों है?" में LongRunningOperation async विधि?
बेनिसन सैम

3
@codea एरिक लिपर्ट की टिप्पणी के लेख में उन्होंने इस विषय पर एक परिचयात्मक लेख को जोड़ा जहां वह विशेष रूप से DoEvents रणनीति की तुलना async-wait के साथ करते हैं
कैमिलो मार्टिनेज

13
@ बेंसनसम धागा थोड़ा पुराना है, लेकिन मेरे पास एक ही सवाल था और जवाब की तलाश में था। "प्रतीक्षा" का कारण यह है कि यदि हम "प्रतीक्षा" को छोड़ देते हैं तो LongRunningOperationAsync () तुरंत वापस आ जाएगा। वास्तव में कंपाइलर एक चेतावनी देगा यदि हम प्रतीक्षा को हटा दें। स्टीफन क्लीरी का ब्लॉग पोस्ट blog.stephencleary.com/2011/09/… डिजाइन चर्चाओं का एक निष्पादन प्रदान करता है।
शेल्बीपेरेरा

70
अगर हर एस्किंट मेथड में इसके अंदर एक वेट की जरूरत है, और एस्किट केवल एस्कॉन के तरीकों पर किया जा सकता है, यह कब बंद होता है?
ब्रूनो सैंटोस

108
यह उत्तर स्पष्ट रूप से गलत है। ये कई upvotes कई उपयोगकर्ताओं के लिए गलत समझ का कारण बनेंगे। MS दस्तावेज़ीकरण स्पष्ट रूप से कहता है, किसी अन्य धागे का उपयोग तब नहीं किया जाता है जब बस async, प्रतीक्षा करें। msdn.microsoft.com/en-us/library/mt674882.aspx कृपया किसी ने उत्तर को सही किया। इसके कारण मैंने पूरा एक दिन बर्बाद कर दिया।
कृष्ण दीपक

171

मेरी समझ से मुख्य चीजों में से एक async और इंतजार करना है जो कोड को लिखने और पढ़ने में आसान बनाता है।

वे एसिंक्रोनस कोड को लिखना और पढ़ना आसान बनाते हैं, हाँ।

क्या लंबी अवधि के तर्क प्रदर्शन करने के लिए पृष्ठभूमि के धागे के रूप में एक ही बात है?

हर्गिज नहीं।

// मुझे समझ नहीं आ रहा है कि इस पद्धति को 'async' के रूप में चिह्नित क्यों किया जाना चाहिए।

asyncकीवर्ड के लिए सक्षम बनाता awaitकीवर्ड। इसलिए किसी भी विधि का उपयोग awaitकरना चाहिए async

// DoSomethingAsync () विधि से 5 सेकंड की नींद के बाद इस लाइन तक पहुंचा जाता है। क्या इसे तुरंत नहीं पहुंचाया जाना चाहिए?

नहीं, क्योंकि asyncविधियाँ डिफ़ॉल्ट रूप से किसी अन्य थ्रेड पर नहीं चलती हैं।

// क्या यह एक पृष्ठभूमि थ्रेड पर निष्पादित है?

नहीं।


आपको मेरा async/ awaitइंट्रो मददगार लग सकता है। सरकारी MSDN डॉक्स भी असामान्य रूप से अच्छा (विशेष रूप से कर रहे हैं नल अनुभाग), और asyncटीम एक उत्कृष्ट बाहर डाल पूछे जाने वाले प्रश्न


6
तो यह एक पृष्ठभूमि थ्रेड पर नहीं चल रहा है, लेकिन यह भी ब्लॉक नहीं करता है। यह एसिंक्रोनस एपीआई की वजह से संभव है जो थ्रेड्स के साथ करतब दिखाने के बजाय कॉलबैक का उपयोग करते हैं। आप एक (I / O, सॉकेट, ..) का संचालन करते हैं और अपनी चीजों को करने के लिए वापस लौटते हैं। जब ऑपरेशन किया जाता है, तो ओएस कॉलबैक को लागू करेगा। यह Node.js या पायथन ट्विस्टेड फ्रेमवर्क करता है और उनकी कुछ अच्छी व्याख्या भी है।
रोमन प्लासील

3
"Async कीवर्ड प्रतीक्षित कीवर्ड को सक्षम करता है। इसलिए किसी भी तरीके का उपयोग करने के लिए async चिह्नित होना चाहिए।", - लेकिन क्यों? यह उत्तर यह समझने में मदद नहीं करता है कि पद्धति को async के रूप में चिह्नित क्यों किया जाना चाहिए। क्या कंपाइलर केवल यह अनुमान नहीं लगा सकता है कि प्रतीक्षारत खोजशब्दों के लिए अंदर की ओर देखने के लिए विधि एसिंक्स है?
स्टैनिस्लाव

9
@Stanislav: मेरे पास एक ब्लॉग प्रविष्टि है जो उस प्रश्न को संबोधित करती है।
स्टीफन क्लीरी

3
सुझाया गया स्पष्टीकरण: नहीं, क्योंकि asyncडिफ़ॉल्ट रूप से किसी अन्य थ्रेड पर विधियाँ नहीं चलती हैं। आपके उदाहरण में, Sleep()कॉल DoSomethingAsync()वर्तमान थ्रेड को ब्लॉक करता है जो निष्पादन को पूर्ण button1_Click()होने तक जारी रखने से रोकता है DoSomethingAsync()। ध्यान दें कि Thread.Sleep()निष्पादित धागे को ब्लॉक करते समय,Task.Delay() does not.
डेविड आरआर

166

व्याख्या

यहाँ एक उच्च स्तर पर async/ का एक त्वरित उदाहरण awaitहै। इससे परे विचार करने के लिए बहुत अधिक विवरण हैं।

नोट: Task.Delay(1000)1 सेकंड के लिए काम करने का अनुकरण करता है। मुझे लगता है कि बाहरी संसाधन से प्रतिक्रिया के लिए इंतजार करना सबसे अच्छा है। चूँकि हमारा कोड प्रतिक्रिया की प्रतीक्षा कर रहा है, सिस्टम चल रहे कार्य को साइड में सेट कर सकता है और समाप्त होने के बाद वापस आ सकता है। इस बीच, यह उस धागे पर कुछ अन्य काम कर सकता है।

नीचे दिए गए उदाहरण में, पहला ब्लॉक बिल्कुल ऐसा कर रहा है। यह सभी कार्यों को तुरंत ( Task.Delayलाइनों) शुरू करता है और उन्हें किनारे पर सेट करता है। await aअगली पंक्ति में जाने से पहले 1 सेकंड की देरी होने तक कोड लाइन पर रुक जाएगा । के बाद से b, c, d, और eसभी के रूप में लगभग सटीक एक ही समय में क्रियान्वित करने शुरू कर दिया a(इंतजार कमी के कारण), वे मोटे तौर पर इस मामले में एक ही समय में खत्म कर देना चाहिए।

नीचे दिए गए उदाहरण में, दूसरा ब्लॉक एक कार्य शुरू कर awaitरहा है और बाद के कार्यों को शुरू करने से पहले इसे समाप्त करने के लिए इंतजार कर रहा है। इसके प्रत्येक पुनरावृत्ति में 1 सेकंड लगता है। awaitकार्यक्रम से रोकने या जारी रखने से पहले परिणाम के लिए इंतजार कर रहा है। यह पहले और दूसरे ब्लॉक के बीच मुख्य अंतर है।

उदाहरण

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

उत्पादन:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

SynchronizationContext के बारे में अतिरिक्त जानकारी

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

इसका एक पहलू है जो async/ awaitअवधारणा को समझने के लिए कुछ हद तक मुश्किल बना देता है । यह तथ्य है कि इस उदाहरण में, यह सब एक ही धागे पर हो रहा है (या कम से कम इसके संबंध में एक ही धागा प्रतीत होता है SynchronizationContext)। डिफ़ॉल्ट रूप से, awaitमूल थ्रेड के सिंक्रनाइज़ेशन संदर्भ को पुनर्स्थापित करेगा जो यह चल रहा था। उदाहरण के लिए, ASP.NET में आपके पास एक HttpContextऐसा धागा होता है जब कोई अनुरोध आता है। इस संदर्भ में मूल Http अनुरोध के लिए विशिष्ट चीजें शामिल होती हैं जैसे कि मूल अनुरोध वस्तु जिसमें भाषा, आईपी पता, हेडर, आदि जैसी चीजें होती हैं। यदि आप कुछ प्रसंस्करण के माध्यम से थ्रेड्स को आधे रास्ते पर स्विच करते हैं, तो आप संभावित रूप से इस ऑब्जेक्ट की जानकारी को किसी भिन्न पर खींचने की कोशिश कर सकते हैंHttpContextजो विनाशकारी हो सकता है। यदि आप जानते हैं कि आप किसी भी चीज़ के लिए संदर्भ का उपयोग नहीं कर रहे हैं, तो आप इसके बारे में "परवाह नहीं" करना चुन सकते हैं। यह मूल रूप से आपके कोड को इसके साथ संदर्भ को लाए बिना एक अलग थ्रेड पर चलाने की अनुमति देता है।

आप इसे कैसे प्राप्त करेंगे? डिफ़ॉल्ट रूप से, await a;कोड वास्तव में एक धारणा बना रहा है कि आप संदर्भ को कैप्चर और पुनर्स्थापित करना चाहते हैं:

await a; //Same as the line below
await a.ConfigureAwait(true);

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

await a.ConfigureAwait(false);

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

क्या यह सामान भ्रमित है? जी हाँ! क्या आप इसका पता लगा सकते हैं? शायद! एक बार जब आप अवधारणाओं की समझ है, तो स्टीफन Cleary के स्पष्टीकरण जिनमें से एक तकनीकी समझ के साथ किसी की ओर अधिक सक्षम हो जाते हैं पर चलते async/ awaitपहले से ही।


आइए बताते हैं कि क्या ये सभी कार्य एक इंटेंस रिटर्न दे रहे हैं और अगर मैं पहले कार्य के परिणाम को दूसरे कार्य (या कुछ गणना) में उपयोग कर रहा हूं तो यह गलत होगा?
वीरेंद्र गुप्ता

3
@ वीरेंद्रगुप्त हाँ। आप जानबूझकर उन्हें उस मामले में अतुल्यकालिक रूप से नहीं चलाने का चयन करेंगे (क्योंकि वे अतुल्यकालिक नहीं हैं)। कॉन्फ़िगरेशन संदर्भ के बारे में महसूस करने के लिए कुछ अन्य चीजें भी हैं जो मैं यहां नहीं जाऊंगा
जो फिलिप्स

तो await MethodCall()एक निरपेक्ष बर्बादी है? आप await/ साथ ही ड्रॉप कर सकते हैं async?
Vitani

2
@ जोकी बिलकुल नहीं। जब आप फोन करते हैं await, मुझे लगता है कि यह थ्रेड को पूल में वापस रखने के बजाय रिलीज करता है। यह टास्क की वापसी का इंतजार करते हुए इसे अन्यत्र उपयोग के लिए उपलब्ध करवाता है
जो फिलिप्स

2
@JoePhillips मुझे लगता है कि आपने जो कहा है, वह एसिंक्स / वेट का सार है। कॉलिंग थ्रेड को मुक्त किया जाता है और मशीन पर अन्य प्रक्रियाओं द्वारा उपयोग किया जा सकता है। जब वेट कॉल पूरी हो जाती है, तो मूल रूप से कॉल करने वाले को फिर से शुरू करने के लिए एक नए धागे का उपयोग किया जाता है। कॉलर अभी भी इंतजार कर रहा है, लेकिन लाभ यह है कि इस बीच एक धागा मुक्त हो जाता है। यह async / प्रतीक्षा का लाभ है?
बॉब हॉर्न

147

अन्य उत्तरों के लिए, प्रतीक्षा पर एक नज़र डालें (C # संदर्भ)

और विशेष रूप से शामिल उदाहरण में, यह आपकी स्थिति को थोड़ा समझाता है

निम्न Windows प्रपत्र उदाहरण एक async विधि, WaitAsynchronouslyAsync में प्रतीक्षा के उपयोग को दिखाता है। WaitSynchronously के व्यवहार के साथ उस विधि के व्यवहार का विरोध करें। किसी कार्य के लिए लागू किए गए प्रतीक्षारत ऑपरेटर के बिना, WaitSynchronously इसकी परिभाषा में Async संशोधक और उसके शरीर में Thread.Sleep करने के लिए एक कॉल के उपयोग के बावजूद सिंक्रनाइज़ चलता है।

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}

3
जवाब के लिए धन्यवाद। लेकिन WaitAsynchronouslyAsync () को एक अलग थ्रेड पर निष्पादित किया जाता है?
दान दिनु

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

13
MSDN के इस लेख के अनुसार , "async और प्रतीक्षित कीवर्ड अतिरिक्त थ्रेड बनाने के लिए कारण नहीं बनते हैं .... एक async विधि अपने स्वयं के थ्रेड पर नहीं चलती है"। मेरी समझ यह है कि कीवर्ड के इंतजार में फ्रेमवर्क आगे (कॉलर को पीछे) छोड़ देता है ताकि लंबे ऑपरेशन के खत्म होने के इंतजार में सभी संभव स्वतंत्र कोड चल सकें। मुझे लगता है कि इसका मतलब है कि एक बार सभी स्वतंत्र कोड चले गए हैं, अगर लंबे ऑपरेशन वापस नहीं आए हैं, तो यह ब्लॉक हो जाएगा। मैं अभी यह सीख रहा हूं, हालांकि।
विम्स

9
@astander यह गलत है। यह एक अलग थ्रेड पर निष्पादित नहीं करता है । यह सिर्फ समयबद्धन (बाकी विधि) को शेड्यूल करता है, जिसे फायरर्स द्वारा इस्तेमाल किया जाने वाला टाइमर कहा जाता है Task.Delay
19

1
नींद के कारण यह उत्तर गलत है। टास्क के साथ स्वीकृत जवाब देखें टास्क।लेय (1000); जिसका सही व्यवहार हो।
जारेड अपडेटाइक

62

एक सरल कंसोल प्रोग्राम में कार्रवाई में उपरोक्त स्पष्टीकरण दिखा रहा है:

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

और आउटपुट है:

Starting Long Running method...
Press any key to exit...
End Long Running method...

इस प्रकार,

  1. मुख्य के माध्यम से लंबे समय से चलने वाली विधि शुरू होती है TestAsyncAwaitMethods। यह तुरंत वर्तमान थ्रेड को रोके बिना वापस आ जाता है और हम तुरंत 'बाहर निकलने के लिए कोई भी कुंजी दबाएं' संदेश देखते हैं
  2. यह सब कुछ, LongRunningMethodपृष्ठभूमि में चल रहा है। एक बार पूरा होने के बाद, थ्रेडपूल से एक और धागा इस संदर्भ को चुनता है और अंतिम संदेश प्रदर्शित करता है

इस प्रकार, धागा अवरुद्ध नहीं है।


"बाहर निकलने के लिए कोई भी कुंजी दबाएं ..." आउटपुट के किस भाग में दिखाया जाएगा?
स्टूडियोएक्स

1
और (वापसी 1) का क्या उपयोग है? क्या ये ज़रूरी हैं?
स्टूडियोएक्स

1
@StudioX मुझे लगता है कि यह वापसी प्रकार पूर्णांक होना चाहिए
Kuba Do

मुझे लगता है कि return 1भाग कुछ और स्पष्टीकरण के हकदार हैं: awaitकीवर्ड आपको अंतर्निहित प्रकार को Task<T>सीधे वापस करने की अनुमति देता है , इस प्रकार यह आपके एक्साइटिंग कोड को प्रतीक्षित / async दुनिया में अनुकूलित करना आसान बनाता है । लेकिन आपको एक मूल्य वापस नहीं करना है, क्योंकि Taskरिटर्निंग प्रकार को निर्दिष्ट किए बिना वापस करना संभव है , जो एक तुल्यकालिक voidविधि के बराबर होगा । ध्यान रखें कि C # async voidविधियों की अनुमति देता है, लेकिन आपको ऐसा करने से बचना चाहिए जब तक कि आप ईवेंट हैंडलर से नहीं निपटते।
क्रिस्टियानो चुंबन

41

मुझे लगता है कि आपने एक बुरा उदाहरण चुना है System.Threading.Thread.Sleep

एक asyncकार्य का बिंदु मुख्य थ्रेड को लॉक किए बिना पृष्ठभूमि में निष्पादित करने देना है, जैसे कि करनाDownloadFileAsync

System.Threading.Thread.Sleep कुछ ऐसा नहीं है जो "किया जा रहा है", यह सिर्फ सोता है, और इसलिए आपकी अगली पंक्ति 5 सेकंड के बाद पहुंच जाती है ...

इस लेख को पढ़ें, मुझे लगता है कि यह asyncऔर awaitअवधारणा की एक महान व्याख्या है : http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx


3
नींद क्यों खराब उदाहरण है लेकिन डाउनलोड अच्छा उदाहरण है। यह FooBar तरह की बात है जब मैं थ्रेड देखता हूं। सो मुझे समझ में आता है कि कुछ काम है जो समय लेता है। मुझे लगता है कि उनका प्रश्न प्रासंगिक है
अब्दुर्रहीम

1
@ अब्दुर्रहीम Thread.Sleepधागे को रोकता है (धागा कुछ और नहीं कर सकता है, लेकिन बेकार है), लेकिन एक async विधि नहीं है। के मामले में DownloadFileAsync, थ्रेड दूरस्थ सर्वर से उत्तर आने तक कुछ और कर सकता है। एक async विधि में "कुछ कार्य जो समय लेता है" के लिए एक बेहतर प्लेसहोल्डर है Task.Delay, क्योंकि यह वास्तव में अतुल्यकालिक है।
गेब्रियल लुसी

@ गैब्रिएल्यूसी मेरी आपत्ति देरी बनाम नींद के बारे में नहीं है; आपका जवाब एक स्ट्रोमैन के जवाब की तरह लग रहा है; यदि आप इस प्रश्न के लिए एक टिप्पणी के रूप में डालते हैं, तो ऐसा कुछ भी नहीं होगा जिसे मैं आपत्ति नहीं कर सकता, लेकिन एक उत्तर के रूप में यह एक स्ट्रोमैन के उत्तर की तरह महक रहा है। मुझे लगता है कि अभी भी async का उपयोग करना ठीक है, यहाँ तक कि वह जो भी कॉल करता है, उसे कॉल को ब्लॉक करना होगा; यह सभी उद्देश्य को अमान्य नहीं करेगा ... यहां तक ​​कि सभी बचे हुए सिंटैक्टिक चीनी होंगे जो इसे एक वैध मामले के रूप में गिना जाता है,
अब्दुर्रहीम

1
यह मेरा जवाब नहीं था। लेकिन अपनी बात को संबोधित करने के लिए: यह विधि के उद्देश्य पर निर्भर करता है। अगर वह सिर्फ फोन करने के लिए एक विधि चाहता था, तो वह सफल रहा। लेकिन इस मामले में वह एक ऐसा तरीका बनाने की कोशिश कर रहा था जो अतुल्यकालिक रूप से चलता है। उसने केवल asyncकीवर्ड का उपयोग करके ऐसा किया । लेकिन उनकी विधि अभी भी समकालिक रूप से चली, और इस उत्तर ने पूरी तरह से समझाया कि क्यों: क्योंकि वह वास्तव में किसी भी अतुल्यकालिक कोड को नहीं चलाते थे। चिह्नित किए गए तरीके asyncअभी भी सिंक्रोनाइज़ करते हैं जब तक कि आप awaitअधूरे नहीं हैं Task। यदि कोई नहीं है await, तो विधि तुल्यकालिक रूप से चलती है, और संकलक आपको चेतावनी देगा।
गेब्रियल लुसी

23

यहां एक त्वरित कंसोल प्रोग्राम है, जो इसे फॉलो करने वालों को स्पष्ट करता है। TaskToDoविधि अपने लंबी चलने वाली विधि है कि आप async बनाना चाहते है। इसे चलाने के लिए async TestAsyncविधि द्वारा किया जाता है । परीक्षण लूप विधि केवल TaskToDoकार्यों के माध्यम से चलती है और उन्हें async चलाता है। आप परिणामों में देख सकते हैं क्योंकि वे रन से रन तक एक ही क्रम में पूरा नहीं करते हैं - वे कंसोल यूआई थ्रेड को पूरा होने पर रिपोर्ट कर रहे हैं। सरलीकृत, लेकिन मुझे लगता है कि सरलीकृत उदाहरण अधिक सम्मिलित उदाहरणों से बेहतर पैटर्न के मूल को सामने लाते हैं:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}

20

सबसे तेज सीखने के लिए ।।

  • विधि निष्पादन प्रवाह (एक आरेख के साथ) को समझें: 3 मिनट

  • प्रश्न आत्मनिरीक्षण (सीखने के लिए): 1 मिनट

  • जल्दी से वाक्य रचना चीनी के माध्यम से प्राप्त करें: 5 मिनट

  • एक डेवलपर का भ्रम साझा करें: 5 मिनट

  • समस्या: सामान्य कोड के वास्तविक विश्व कार्यान्वयन को जल्दी से बदलकर Async कोड: 2 मिनट

  • जहाँ से अगला?

विधि निष्पादन प्रवाह (एक आरेख के साथ) को समझें: 3 मिनट

इस छवि में, # 6 पर ध्यान केंद्रित करें (इससे ज्यादा कुछ नहीं) यहां छवि विवरण दर्ज करें

# 6 कदम पर: निष्पादन यहां रुक गया क्योंकि यह काम से बाहर चला गया है। इसे जारी रखने के लिए getStringTask (एक फ़ंक्शन का प्रकार) से परिणाम की आवश्यकता है। इसलिए, यह awaitअपनी प्रगति को निलंबित करने के लिए और कॉल करने वाले को वापस (उपज) देने के लिए एक ऑपरेटर का उपयोग करता है (इस पद्धति का हम जिस में हैं)। GetStringTask पर वास्तविक कॉल पहले # 2 में की गई थी। # 2 पर एक स्ट्रिंग परिणाम वापस करने का वादा किया गया था। लेकिन इसका परिणाम कब लौटेगा? क्या हमें (# 1: AccessTheWebAsync) दोबारा कॉल करना चाहिए? परिणाम किसे मिलता है, # 2 (कॉलिंग स्टेटमेंट) या # 6 (स्टेटमेंट का इंतजार)

AccessTheWebAsync () का बाहरी कॉलर भी अब इंतजार कर रहा है। तो AccessTheWebAsync, और AccessTheWebAsync की प्रतीक्षा कर रहा कॉलर फिलहाल GetStringAsync की प्रतीक्षा कर रहा है। दिलचस्प बात यह है कि AccessTheWebAsync ने प्रतीक्षा करने से समय बचाने के लिए प्रतीक्षा करने से पहले (# 4) कुछ काम किया। मल्टीटास्क के लिए समान स्वतंत्रता बाहरी कॉलर (और श्रृंखला में सभी कॉलर्स) के लिए भी उपलब्ध है और यह इस 'async' चीज़ का सबसे बड़ा प्लस है! आपको ऐसा लगता है कि यह सिंक्रोनस है..या सामान्य है लेकिन ऐसा नहीं है।

याद रखें, विधि पहले ही (# 2) वापस आ गई थी, यह फिर से नहीं लौट सकती है (दूसरी बार नहीं)। तो फोन करने वाले को कैसे पता चलेगा? यह सभी कार्य के बारे में है ! टास्क पास किया गया। टास्क का इंतजार किया गया (विधि नहीं, मूल्य नहीं)। मान टास्क में सेट किया जाएगा। कार्य स्थिति को पूरा करने के लिए सेट किया जाएगा। कॉलर सिर्फ टास्क (# 6) पर नजर रखता है। तो 6 # का जवाब है जहां / जो परिणाम प्राप्त करता है। आगे के लिए यहाँ पढ़ता हूँ ।

सीखने की खातिर सवाल आत्मनिरीक्षण: 1 मिनट

आइए हम प्रश्न को थोड़ा समायोजित करें:

कैसे और कब उपयोग करें और ? asyncawait Tasks

क्योंकि सीखने को Taskस्वचालित रूप से अन्य दो शामिल हैं (और आपके प्रश्न का उत्तर देते हैं)

जल्दी से वाक्य रचना चीनी के माध्यम से प्राप्त करें: 5 मिनट

  • रूपांतरण से पहले (मूल विधि)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • उपरोक्त विधि को कॉल करने के लिए एक कार्य-ified विधि

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

क्या हमने प्रतीक्षा या एसिंक्स का उल्लेख किया है? नहीं। उपरोक्त विधि को कॉल करें और आपको एक कार्य मिलता है जिसे आप मॉनिटर कर सकते हैं। आप पहले से ही जानते हैं कि कार्य क्या देता है .. एक पूर्णांक।

  • टास्क को कॉल करना थोड़ा मुश्किल है और यह तब होता है जब कीवर्ड दिखाई देने लगते हैं। हमें MethodTask कहते हैं ()

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

नीचे दिए गए चित्र के रूप में समान कोड जोड़ा गया है: यहां छवि विवरण दर्ज करें

  1. हम कार्य समाप्त होने की प्रतीक्षा कर रहे हैं। इसलिएawait
  2. चूंकि हम प्रतीक्षा का उपयोग करते हैं, हमें async(अनिवार्य वाक्यविन्यास) का उपयोग करना चाहिए
  3. MethodAsync Asyncउपसर्ग के रूप में (कोडिंग मानक)

awaitसमझना आसान है लेकिन शेष दो ( async, Async) नहीं हो सकते हैं :)। हालांकि, इसे कंपाइलर के लिए बहुत अधिक अर्थ देना चाहिए। हालांकि बाद में यहां पढ़ा जाता है

तो 2 भाग हैं।

  1. 'कार्य' बनाएँ
  2. कार्य को कॉल करने के लिए सिंटैक्टिक चीनी बनाएं ( await+async)

याद रखें, हमारे पास AccessTheWebAsync () के लिए एक बाहरी कॉलर था और उस कॉलर को या तो नहीं बख्शा गया है ... यानी इसे await+asyncभी उसी की आवश्यकता है। और सिलसिला जारी है। लेकिन हमेशा Taskएक छोर पर रहेगा ।

सब ठीक है, लेकिन एक डेवलपर को # 1 (टास्क) गायब देखकर आश्चर्य हुआ ...

एक डेवलपर का भ्रम साझा करें: 5 मिनट

एक डेवलपर ने लागू नहीं करने की गलती की है Taskलेकिन यह अभी भी काम करता है! प्रश्न और बस प्रदान किए गए उत्तर को समझने की कोशिश करें । आशा है कि आप पढ़ चुके होंगे और पूरी तरह से समझ गए होंगे। सारांश यह है कि हम 'टास्क' को देख / कार्यान्वित नहीं कर सकते हैं, लेकिन इसे कहीं न कहीं मूल वर्ग में लागू किया जाता है। इसी तरह हमारे उदाहरण में पहले से ही कॉल किया गया MethodAsync()तरीका उस तरीके को लागू करने की तुलना में आसान है जो हमारे Task( ए MethodTask()) के साथ है । अधिकांश डेवलपर्स Tasksको असिंक्रोनस कोड को कोड कन्वर्ट करते समय अपना सिर इधर-उधर करना मुश्किल होता है ।

सुझाव: कठिनाई को आउटसोर्स करने के लिए एक मौजूदा Async कार्यान्वयन (जैसे MethodAsyncया ToListAsync) खोजने का प्रयास करें । इसलिए हमें केवल Async और प्रतीक्षा से निपटने की आवश्यकता है (जो कि सामान्य कोड के समान आसान और सुंदर है)

समस्या: सामान्य कोड के वास्तविक विश्व कार्यान्वयन को जल्दी से बदलकर Async ऑपरेशन: 2 मिनट

डेटा लेयर में नीचे दिखाई गई कोड लाइन (कई जगह) टूटने लगी। क्योंकि हमने अपने कुछ कोड .Net फ्रेमवर्क 4.2 से * .Net कोर में अपडेट कर दिए हैं। हम सभी को आवेदन में 1 घंटे में इसे ठीक करना था!

var myContract = query.Where(c => c.ContractID == _contractID).First();

बहुत आसान!

  1. हमने EntityFramework नगेट पैकेज स्थापित किया क्योंकि इसमें QueryableExtensions है। या दूसरे शब्दों में यह Async कार्यान्वयन (कार्य) करता है, इसलिए हम सरल Asyncऔर awaitकोड में जीवित रह सकते हैं ।
  2. नाम स्थान = Microsoft.EntityFrameworkCore

कॉलिंग कोड लाइन इस तरह बदल गई

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. विधि हस्ताक्षर से परिवर्तित

    Contract GetContract(int contractnumber)

    सेवा

    async Task<Contract> GetContractAsync(int contractnumber)

  2. कॉलिंग विधि भी प्रभावित हुई: GetContractAsync(123456);इसे कहा गयाGetContractAsync(123456).Result;

  3. हमने 30 मिनट में इसे हर जगह बदल दिया!

लेकिन आर्किटेक्ट ने हमें बताया कि इसके लिए सिर्फ EntityFramework पुस्तकालय का उपयोग न करें! उफ़! नाटक! फिर हमने एक कस्टम टास्क कार्यान्वयन (yuk) किया। जो आप जानते हैं कि कैसे। फिर भी आसान! ।।स्तरीय युक ।।

जहाँ से अगला? एक शानदार त्वरित वीडियो है जिसे हम ASP.Net कोर में एसिंक्रोनस के लिए सिंक्रोनस कॉल्स को परिवर्तित करने के बारे में देख सकते हैं , शायद यह संभव है कि इसे पढ़ने के बाद एक दिशा होगी।


शानदार जवाब! इससे मुझे एक टन की मदद मिली
cklimowski

1
अच्छा उत्तर। तुम बस की तरह छोटी बातों के एक जोड़े को ठीक करना चाह सकते हैं: (क) "नेट ढांचा 4.2" का वर्णन (जैसे कोई संस्करण है कि मैं के बारे में पता, मौजूद है) (ख) में EntityFrameWork => EntityFramework आवरण
immitev

15

यहाँ सभी उत्तर फ़ंक्शन Task.Delay()या कुछ अन्य निर्मित asyncफ़ंक्शन का उपयोग करते हैं। लेकिन यहाँ मेरा उदाहरण है कि उन asyncकार्यों में से कोई भी उपयोग नहीं है :

// Starts counting to a large number and then immediately displays message "I'm counting...". 
// Then it waits for task to finish and displays "finished, press any key".
static void asyncTest ()
{
    Console.WriteLine("Started asyncTest()");
    Task<long> task = asyncTest_count();
    Console.WriteLine("Started counting, please wait...");
    task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
    //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
    Console.WriteLine("Finished counting.");
    Console.WriteLine("Press any key to exit program.");
    Console.ReadLine();
}

static async Task<long> asyncTest_count()
{
    long k = 0;
    Console.WriteLine("Started asyncTest_count()");
    await Task.Run(() =>
    {
        long countTo = 100000000;
        int prevPercentDone = -1;
        for (long i = 0; i <= countTo; i++)
        {
            int percentDone = (int)(100 * (i / (double)countTo));
            if (percentDone != prevPercentDone)
            {
                prevPercentDone = percentDone;
                Console.Write(percentDone.ToString() + "% ");
            }

            k = i;
        }
    });
    Console.WriteLine("");
    Console.WriteLine("Finished asyncTest_count()");
    return k;
}

2
धन्यवाद! पहला उत्तर जो वास्तव में प्रतीक्षा के बजाय कुछ काम करता है।
जेफनल जुएल

दिखाने के लिए धन्यवाद task.Wait();और यह कैसे async / नरक से बचने के लिए इस्तेमाल किया जा सकता है: P
एनकोडर

12

यह उत्तर ASP.NET के लिए विशिष्ट कुछ जानकारी प्रदान करना है।

MVC नियंत्रक में async / प्रतीक्षा का उपयोग करके, थ्रेड पूल का उपयोग बढ़ाना संभव है और बेहतर थ्रूपुट प्राप्त करना संभव है, जैसा कि नीचे दिए गए लेख में बताया गया है,

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

वेब एप्लिकेशन में जो स्टार्ट-अप पर बड़ी संख्या में समवर्ती अनुरोधों को देखता है या जिसमें एक बर्फ़ीली लोड होता है (जहां समवर्ती अचानक बढ़ जाती है), इन वेब सेवा कॉलों को अतुल्यकालिक बनाने से आपके एप्लिकेशन की जवाबदेही बढ़ जाएगी। एक एसिंक्रोनस अनुरोध एक समकालिक अनुरोध के रूप में संसाधित होने में उतना ही समय लेता है। उदाहरण के लिए, यदि कोई अनुरोध एक वेब सेवा कॉल करता है जिसे पूरा करने के लिए दो सेकंड की आवश्यकता होती है, तो अनुरोध को दो सेकंड लगते हैं चाहे वह सिंक्रोनाइज़ या असिंक्रोनस रूप से किया गया हो। हालांकि, एक अतुल्यकालिक कॉल के दौरान, एक धागा अन्य अनुरोधों के जवाब देने से अवरुद्ध नहीं होता है, जबकि यह पहले अनुरोध को पूरा होने का इंतजार करता है। इसलिए, अतुल्यकालिक अनुरोध अनुरोध कतारबद्ध और थ्रेड पूल वृद्धि को रोकते हैं जब कई समवर्ती अनुरोध होते हैं जो लंबे समय तक चलने वाले संचालन को आमंत्रित करते हैं।


12

Async और Await सरल व्याख्या

सरल सादृश्य

एक व्यक्ति अपनी सुबह की ट्रेन की प्रतीक्षा कर सकता है । यह सब वे कर रहे हैं क्योंकि यह उनका प्राथमिक कार्य है जो वे वर्तमान में कर रहे हैं। (तुल्यकालिक प्रोग्रामिंग (आप आमतौर पर क्या करते हैं!)

एक अन्य व्यक्ति अपनी सुबह की ट्रेन का इंतजार कर सकता है, जबकि वह सिगरेट पीता है और फिर अपनी कॉफी पीता है। (अतुल्यकालिक प्रोग्रामिंग)

अतुल्यकालिक प्रोग्रामिंग क्या है?

एसिंक्रोनस प्रोग्रामिंग वह जगह है जहां एक प्रोग्रामर निष्पादन के मुख्य धागे से एक अलग थ्रेड पर अपना कुछ कोड चलाने का चयन करेगा और उसके पूरा होने पर मुख्य थ्रेड को सूचित करेगा।

वास्तव में एसिंक्स कीवर्ड क्या करता है?

Async कीवर्ड को विधि नाम की तरह उपसर्ग करना

async void DoSomething(){ . . .

प्रोग्रामर को एसिंक्रोनस कार्यों को कॉल करते समय प्रतीक्षित कीवर्ड का उपयोग करने की अनुमति देता है। बस इतना ही करता है।

यह महत्वपूर्ण क्यों है?

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

आप Async और Await का उपयोग कब करते हैं?

आदर्श रूप से अतुल्यकालिक कीवर्ड का उपयोग करें जब आप ऐसा कुछ भी कर रहे हैं जिसमें उपयोगकर्ता इंटरफ़ेस शामिल नहीं है।

तो चलिए बताते हैं कि आप एक ऐसा प्रोग्राम लिख रहे हैं जो उपयोगकर्ता को अपने मोबाइल फोन पर स्केच करने की अनुमति देता है लेकिन हर 5 सेकंड में यह इंटरनेट पर मौसम की जाँच करने वाला है।

हमें मौसम को पाने के लिए नेटवर्क पर हर 5 सेकंड में कॉलिंग पोलिंग कॉल का इंतजार करना चाहिए क्योंकि एप्लिकेशन के उपयोगकर्ता को सुंदर चित्रों को खींचने के लिए मोबाइल टच स्क्रीन के साथ बातचीत करते रहना चाहिए।

आप Async और Await का उपयोग कैसे करते हैं

ऊपर दिए गए उदाहरण के बाद, यहाँ कुछ छद्म कोड है कि इसे कैसे लिखा जाए:

    //ASYNCHRONOUS
    //this is called using the await keyword every 5 seconds from a polling timer or something.

    async Task CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }

अतिरिक्त नोट्स - अपडेट

मैं अपने मूल नोटों में उल्लेख करना भूल गया कि C # में आप केवल टास्क में लिपटे हुए तरीकों का इंतजार कर सकते हैं। उदाहरण के लिए आप इस विधि का इंतजार कर सकते हैं:

// awaiting this will return a string.
// calling this without await (synchronously) will result in a Task<string> object.
async Task<string> FetchHelloWorld() {..

आप उन तरीकों का इंतजार नहीं कर सकते जो इस तरह के कार्य नहीं हैं:

async string FetchHelloWorld() {..

यहां टास्क क्लास के लिए स्रोत कोड की समीक्षा करने के लिए स्वतंत्र महसूस करें ।


4
यह एक लिखने के लिए समय निकालने के लिए धन्यवाद।
प्रशांत

10

Async / Await

वास्तव में Async / Await कीवर्ड की एक जोड़ी है, जो एक अतुल्यकालिक कार्य के कॉलबैक बनाने के लिए सिर्फ वाक्य रचना चीनी है।

उदाहरण के लिए इस ऑपरेशन को लें:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

ऊपर दिए गए कोड में कई नुकसान हैं। त्रुटियों को पारित नहीं किया जाता है और इसे पढ़ना मुश्किल है। लेकिन Async और Await हमारी मदद करने के लिए आते हैं:

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

Await कॉल को Async विधियों में होना चाहिए। इसके कुछ फायदे हैं:

  • कार्य का परिणाम देता है
  • स्वचालित रूप से कॉलबैक बनाता है
  • त्रुटियों की जाँच करता है और उन्हें कॉलस्टैक में बबल देता है (केवल कॉलस्टैक में कोई भी प्रतीक्षा नहीं करता है)
  • परिणाम की प्रतीक्षा करता है
  • मुख्य धागे को मुक्त करता है
  • मुख्य धागे पर कॉलबैक चलाता है
  • कार्य के लिए थ्रेडपूल से एक कार्यकर्ता थ्रेड का उपयोग करता है
  • कोड को पढ़ना आसान बनाता है
  • और भी काफी

नोट : Async और Await का उपयोग अतुल्यकालिक कॉल के साथ किया जाता है ताकि ये न बन सकें। आपको इसके लिए टास्क लिबरी का उपयोग करना होगा, जैसे टास्क.रुण ()।

यहाँ प्रतीक्षा और कोई भी प्रतीक्षा समाधान के बीच तुलना है

यह कोई भी async समाधान नहीं है:

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

यह इस async विधि है:

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

आप वास्तव में प्रतीक्षित कीवर्ड के बिना एक async विधि कह सकते हैं लेकिन इसका मतलब यह है कि यहाँ कोई अपवाद रिलीज़ मोड में निगल लिया गया है:

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

Async और Await समानांतर कंप्यूटिंग के लिए नहीं हैं। उनका उपयोग आपके मुख्य धागे को अवरुद्ध करने के लिए नहीं किया जाता है। जब यह asp.net या Windows अनुप्रयोगों के बारे में है, तो नेटवर्क कॉल के कारण अपने मुख्य धागे को अवरुद्ध करना एक बुरी बात है। यदि आप ऐसा करते हैं, तो आपका ऐप अप्रतिसादी या दुर्घटनाग्रस्त हो जाएगा।

अधिक उदाहरण के लिए एमएस डॉक्स देखें ।


9

सच कहूं तो मुझे अब भी सबसे अच्छा स्पष्टीकरण भविष्य के बारे में और वादों के बारे में है: http://en.wikipedia.org/wiki/Futures_and_promises

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

वहां से आप चीजों को ऑप्टिमाइज़ कर सकते हैं: कुछ ऑपरेशंस को एसिंक्स पर लागू किया जा सकता है और आप बाद के अनुरोधों और उन्हें पुनः व्यवस्थित करके फ़ाइल IO और नेटवर्क संचार जैसी चीजों को ऑप्टिमाइज़ कर सकते हैं। मुझे यकीन नहीं है कि यह पहले से ही Microsoft के कार्य ढांचे में है - लेकिन अगर ऐसा नहीं है तो मैं पहली चीजों में से एक होगा जो मैं जोड़ूंगा।

आप वास्तव में सी # 4.0 में पैदावार के साथ भविष्य के पैटर्न को लागू कर सकते हैं। यदि आप जानना चाहते हैं कि यह कैसे ठीक से काम करता है, तो मैं इस लिंक की सिफारिश कर सकता हूं जो एक अच्छा काम करता है: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ । हालाँकि, यदि आप इसके साथ अपने आप जुड़ना शुरू करते हैं, तो आप देखेंगे कि यदि आप सभी अच्छी चीजें करना चाहते हैं, तो आपको वास्तव में भाषा के समर्थन की आवश्यकता है - जो कि Microsoft ने ठीक किया।


8

इस कंसोल को देखें https://dotnetfiddle.net/VhZdLU (और यदि संभव हो तो इसे सुधारें) एक साधारण कंसोल एप्लिकेशन को चलाने के लिए जो टास्क, टास्क। व्हाट्सएप (), async और उसी प्रोग्राम में ऑपरेटरों का इंतजार करता है

इस फिडेल को आपके निष्पादन चक्र की अवधारणा को साफ करना चाहिए।

यहाँ नमूना कोड है

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

आउटपुट विंडो से आने वाला ट्रेस: यहां छवि विवरण दर्ज करें


3
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}

3

उच्च स्तर पर:

1) Async कीवर्ड प्रतीक्षा को सक्षम करता है और यही सब कुछ करता है। Async कीवर्ड एक अलग थ्रेड में विधि नहीं चलाता है। शुरुआत च async विधि समकालिक रूप से चलती है जब तक कि यह एक समय लेने वाले कार्य पर इंतजार नहीं करता है।

2) आप एक ऐसी विधि का इंतजार कर सकते हैं, जो टाइप T के टास्क या टास्क को लौटाता है। आप async शून्य विधि पर इंतजार नहीं कर सकते।

3) पल मुख्य थ्रेड का सामना समय लेने वाले कार्य पर होता है या जब वास्तविक कार्य शुरू किया जाता है, तो मुख्य थ्रेड वर्तमान विधि के कॉलर पर वापस आ जाता है।

4) यदि मुख्य धागा उस कार्य पर प्रतीक्षा करता है जो अभी भी निष्पादित हो रहा है, तो वह इसके लिए प्रतीक्षा नहीं करता है और वर्तमान पद्धति के कॉलर पर लौटता है। इस तरह, एप्लिकेशन उत्तरदायी रहता है।

5) प्रसंस्करण कार्य पर इंतजार, अब थ्रेड पूल से एक अलग थ्रेड पर निष्पादित करेगा।

6) जब यह प्रतीक्षा कार्य पूरा हो जाता है, तो नीचे दिए गए सभी कोड को अलग-अलग थ्रेड द्वारा निष्पादित किया जाएगा

नीचे नमूना कोड है। इसे निष्पादित करें और थ्रेड आईडी की जांच करें

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}

2

जिस तरह से मैं समझता हूं कि यह भी है, मिक्स में एक तीसरा शब्द जोड़ा जाना चाहिए Task:।

Async यह एक अतुल्यकालिक विधि है, यह कहने के लिए आपने अपनी योग्यता पर सिर्फ एक क्वालीफायर लगाया है।

Taskasyncफ़ंक्शन की वापसी है । यह एसिंक्रोनस रूप से निष्पादित करता है।

आप awaitएक कार्य। जब कोड निष्पादन इस रेखा तक पहुंचता है, तो नियंत्रण आपके आसपास के मूल फ़ंक्शन के कॉलर पर वापस कूदता है।

यदि इसके बजाय, आप एक asyncफ़ंक्शन (यानी Task) की वापसी को एक चर पर असाइन करते हैं , जब कोड निष्पादन इस रेखा तक पहुंचता है, तो यह आस-पास के फ़ंक्शन में उस रेखा को अतीत में जारी रखता है जबकिTask असिंक्रोनस रूप से निष्पादित होता है।


1

लंबी अवधि तर्क प्रदर्शन करने के लिए उन्हें पृष्ठभूमि के धागे के बराबर उपयोग कर रहा है?

यह लेख MDSN: एसिंक्रोनस के साथ अतुल्यकालिक प्रोग्रामिंग और प्रतीक्षा (C #) इसे स्पष्ट रूप से समझाता है:

Async और प्रतीक्षा कीवर्ड के कारण अतिरिक्त थ्रेड्स का निर्माण नहीं होता है। Async विधियों को मल्टीथ्रेडिंग की आवश्यकता नहीं है क्योंकि एक async विधि अपने स्वयं के थ्रेड पर नहीं चलती है। विधि वर्तमान सिंक्रनाइज़ेशन संदर्भ पर चलती है और विधि सक्रिय होने पर ही थ्रेड पर समय का उपयोग करती है।


1

निम्न कोड में, HttpClient विधि GetByteArrayAsync एक कार्य देता है, getContentsTask। कार्य पूरा होने पर वास्तविक बाइट सरणी का उत्पादन करने का वादा है। GetContentsTask पूरा होने तक SumPageSizesAsync में निष्पादन निलंबित करने के लिए प्रतीक्षा ऑपरेटर को getContentsTask पर लागू किया जाता है। इस बीच, SumPageSizesAsync के कॉलर पर नियंत्रण वापस आ जाता है। जब getContentsTask समाप्त हो जाता है, तो प्रतीक्षित अभिव्यक्ति बाइट सरणी के लिए मूल्यांकन करती है।

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}

1

नीचे कोड है जो डायलॉग खोलकर एक्सेल फाइल पढ़ता है और फिर एसिंक्स का उपयोग करता है और एसिंक्रोनस को चलाने के लिए प्रतीक्षा करता है जो कोड एक्सेल से एक लाइन से एक पढ़ता है और ग्रिड से बांधता है

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}

0

यहाँ उत्तर प्रतीक्षा / async के बारे में एक सामान्य मार्गदर्शन के रूप में उपयोगी हैं। वे इस बात के बारे में भी कुछ विवरण देते हैं कि कैसे इंतजार / async वायर्ड है। मैं आपके साथ कुछ व्यावहारिक अनुभव साझा करना चाहूंगा जो आपको इस डिज़ाइन पैटर्न का उपयोग करने से पहले जानना चाहिए।

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

आप निश्चित रूप से विभिन्न प्रकार के साधनों का उपयोग करते हुए पृष्ठभूमि के धागे का इंतजार कर सकते हैं:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

इन टिप्पणियों के लिए पूरा कोड https://github.com/marcusts/xamarin-forms-annoyances पर है । AwaitAsyncAntipattern.sln नामक समाधान देखें।

GitHub साइट इस विषय पर अधिक विस्तृत चर्चा के लिंक भी प्रदान करती है।


1
मैं जो समझता हूं, async / awaitकॉलबैक के लिए सिंटैक्टिक शुगर है, इसका थ्रेडिंग से कोई लेना-देना नहीं है। msdn.microsoft.com/en-us/magazine/hh456401.aspx यह गैर-सीपीयू बाउंड कोड के लिए है, जैसे इनपुट या देरी का इंतजार करना। Task.Runकेवल सीपीयू-बाउंड कोड के लिए इस्तेमाल किया जाना चाहिए। blog.stephencleary.com/2013/10/…
geometrikal

The term "await" is literal, so whatever thread you call it on will wait for the result of the method before continuing.यह सच नहीं है - शायद आपका मतलब टास्क था। जब आप उपयोग करते हैं await, तो यह शेष विधि को निष्पादित करने के लिए एक निरंतरता के रूप में सेट करता है जब आप प्रतीक्षा-एड पूरा करते हैं। यह उस विधि से बाहर निकलता है जिसका आपने इसमें उपयोग किया था, इसलिए कॉलर जारी रख सकता है। फिर जब वेट-एड लाइन वास्तव में पूरी हो जाती है, तो यह उस विधि के शेष भाग को कुछ धागे (आमतौर पर एक श्रमिक धागा) पर समाप्त कर देता है।
डॉन चीडेल

@geometrikal बहुत ही कोर में, .NET थ्रेड्स को मुक्त करने केasync/await बारे में है । जब आप वास्तव में-async ऑपरेशन (जैसे .NET की File.WriteAsync) करते हैं, तो यह आपके द्वारा उपयोग की जाने वाली शेष विधि को निलंबित कर देता है, इसलिए कॉलर जारी रख सकता है और संभवतः अपना उद्देश्य पूरा कर सकता है। थ्रेड ऑपरेशन के लिए कोई थ्रेड अवरोधक या प्रतीक्षा नहीं है । जब आपके द्वारा संपादित किया गया ऑपरेशन पूरा हो जाता है, तो शेष विधि को एक धागे पर रखा जाता है और निष्पादित किया जाता है (कॉल-बैक विचार के समान)। awaitawaitawaitawaitasync/await
डॉन चीडल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.