2015-11-26 12 views
5

सबसे अच्छा अभ्यास लूप के अंदर संग्रह में सभी async कॉल एकत्र करना है और Task.WhenAll() करें। फिर भी, समझना चाहते हैं कि क्या होता है जब लूप के अंदर await का सामना करना पड़ता है, Task में वापस क्या होगा? async कॉल के बारे में क्या? क्या यह नए कार्यों को बनाएगा और उन्हें क्रमशः पहले से लौटाए गए Task में जोड़ देगा?लूप के अंदर, क्या प्रत्येक एसिंक कॉल कार्य की निरंतरता का उपयोग करके लौटाए गए कार्य में बंधी जाती है?

कोड के अनुसार नीचे

private void CallLoopAsync() 
{ 
    var loopReturnedTask = LoopAsync(); 
} 

private async Task LoopAsync() 
{ 
    int count = 0; 
    while(count < 5) 
    { 
     await SomeNetworkCallAsync(); 
     count++; 
    } 
} 

चरणों मैं

  1. LoopAsync कहा जाता हो जाता है कर रहे हैं
  2. count शून्य हो जायेगा मान लिया है, जबकि पाश, हालत
  3. चेक किया गया है कोड में प्रवेश करती है
  4. SomeNetworkCallAsync कहा जाता है, और लौटाया गया कार्य
  5. का इंतजार है
  6. नई कार्य/awaitable बनाई गई है
  7. नई कार्य, CallLoopAsync()

में लौट जाता है अब प्रदान की पर्याप्त समय रहने के लिए प्रक्रिया, कैसे/की तरह किस तरह से, अगली कोड लाइनों में के लिए है count++ और आगे SomeNetworkCallAsync निष्पादित किया जाएगा?

अपडेट - Jon Hanna और Stephen Cleary के आधार पर:

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

हालांकि वे श्रृंखलित नहीं कर रहे हैं, प्रत्येक कॉल के रूप में हम इस्तेमाल किया है पूरा करने के लिए पिछले कॉल के लिए इंतजार करेंगे await (राज्य एम/सी में, awaiter.GetResult();)। यह बर्ताव करता है के रूप में लगातार पांच कॉल किए गए हैं, तो और वे एक के बाद एक क्रियान्वित कर रहे हैं एक और ( केवल के बाद पिछले कॉल हो जाता है पूरा)।

के बजाय लेखन

private async Task SomeWorkAsync() 
{ 
    await SomeIndependentNetworkCall();// 2 sec to complete 

    var result1 = await GetDataFromNetworkCallAsync(); // 2 sec to complete 
    await PostDataToNetworkAsync(result1); // 2 sec to complete 
} 

यह

private Task[] RefactoredSomeWorkAsync() 
{ 
    var task1 = SomeIndependentNetworkCall();// 2 sec to complete 

    var task2 = GetDataFromNetworkCallAsync() 
    .ContinueWith(result1 => PostDataToNetworkAsync(result1)).Unwrap();// 4 sec to complete 

    return new[] { task1, task2 }; 
} 

लिखा जाना चाहिए तो: अगर यह सही है, हम कैसे हम async calls.For पूर्व रचना कर रहे हैं में थोड़ा और अधिक सावधान रहना होगा कि हम RefactoredSomeWorkAsyncसमांतरता

private async Task CallRefactoredSomeWorkAsync() 
{ 
    await Task.WhenAll(RefactoredSomeWorkAsync());//Faster, 4 sec 
    await SomeWorkAsync(); // Slower, 6 sec 
} 
की संभावना के कारण 2 सेकंड तक तेज हो सकते हैं

क्या यह सही है? - हाँ। के साथ-साथ "async सभी तरह", "कार्य सभी तरह संचित" अच्छी आदत है।इसी चर्चा here

उत्तर

2

करते हैं, अगर उनमें से कोई कीवर्ड मौजूद नहीं था क्या async और await के समान कोड का उत्पादन करने के लिए, कोड की आवश्यकता होगी थोड़ा की तरह:

private struct LoopAsyncStateMachine : IAsyncStateMachine 
{ 
    public int _state; 
    public AsyncTaskMethodBuilder _builder; 
    public TestAsync _this; 
    public int _count; 
    private TaskAwaiter _awaiter; 
    void IAsyncStateMachine.MoveNext() 
    { 
    try 
    { 
     if (_state != 0) 
     { 
     _count = 0; 
     goto afterSetup; 
     } 
     TaskAwaiter awaiter = _awaiter; 
     _awaiter = default(TaskAwaiter); 
     _state = -1; 
    loopBack: 
     awaiter.GetResult(); 
     awaiter = default(TaskAwaiter); 
     _count++; 
    afterSetup: 
     if (_count < 5) 
     { 
     awaiter = _this.SomeNetworkCallAsync().GetAwaiter(); 
     if (!awaiter.IsCompleted) 
     { 
      _state = 0; 
      _awaiter = awaiter; 
      _builder.AwaitUnsafeOnCompleted<TaskAwaiter, TestAsync.LoopAsyncStateMachine>(ref awaiter, ref this); 
      return; 
     } 
     goto loopBack; 
     } 
     _state = -2; 
     _builder.SetResult(); 
    } 
    catch (Exception exception) 
    { 
     _state = -2; 
     _builder.SetException(exception); 
     return; 
    } 
    } 
    [DebuggerHidden] 
    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0) 
    { 
    _builder.SetStateMachine(param0); 
    } 
} 

public Task LoopAsync() 
{ 
    LoopAsyncStateMachine stateMachine = new LoopAsyncStateMachine(); 
    stateMachine._this = this; 
    AsyncTaskMethodBuilder builder = AsyncTaskMethodBuilder.Create(); 
    stateMachine._builder = builder; 
    stateMachine._state = -1; 
    builder.Start(ref stateMachine); 
    return builder.Task; 
} 

(ऊपर क्या होता है जब आप async का उपयोग पर आधारित है और await सिवाय इसके कि का परिणाम नाम मान्य सी # वर्ग या क्षेत्र के नाम नहीं किया जा सकता है कि का उपयोग करता है, कुछ अतिरिक्त विशेषताओं के साथ। अगर अपने MoveNext() एक IEnumerator कि पूरी तरह से अप्रासंगिक नहीं है, की याद दिलाता है व्यवस्था है जिसके द्वारा await और async लागू करने के लिए एक IAsyncStateMachine उत्पादन एक Taskyield कैसे IEnumerator<T> उत्पन्न करता है) के कई तरीकों से समान है।

परिणाम एक भी कार्य जो AsyncTaskMethodBuilder से आता है और (जो छिपा struct कि async का उत्पादन के करीब है) LoopAsyncStateMachine का उपयोग करता है। इसकी MoveNext() विधि को पहली बार कार्य शुरू करने पर बुलाया जाता है। इसके बाद SomeNetworkCallAsync पर एक प्रतीक्षाकर्ता का उपयोग किया जाएगा। यदि यह पहले ही पूरा हो चुका है तो यह अगले चरण (वृद्धि count और इसी तरह) पर चलता है, अन्यथा यह एक क्षेत्र में प्रतीक्षाकर्ता को संग्रहीत करता है। बाद के उपयोगों पर इसे कॉल किया जाएगा क्योंकि SomeNetworkCallAsync() कार्य वापस आ गया है, और यह परिणाम प्राप्त होगा (जो इस मामले में शून्य है, लेकिन मान लौटाए जाने पर मूल्य हो सकता है)। फिर यह आगे की लूप का प्रयास करता है और फिर जब वह उस कार्य पर इंतजार कर रहा है जो अभी तक पूरा नहीं हुआ है तो वापस लौटाता है।

5 का एक count यह SetResult() बिल्डर, जो Task का परिणाम है कि LoopAsync लौटा था सेट पर कॉल करने पर यह अंत में पहुँच जाता है।

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

3

जब गिनती शून्य है, नया कार्य इंतजार है क्योंकि बनाया जाएगा और उसे वापस लिया जा

नहीं, ये नहीं होगा है। परिणामस्वरूप भंडारण या लौटने के बिना, यह केवल async विधि को कॉल करेगा। loopReturnedTask में से संबंधित LoopAsync के Task को संग्रहीत करेगा।

await SomeNetworkCallAsync(); // call, wait and forget the result 

आप MSDN article on async\await पढ़ना चाहेंगे।

+0

मैं स्वीकार करने के लिए है कि ओपी को यह समझाने की कोशिश करते हुए, मैं बन गए हैं की जरूरत है अपने आप उलझन में। यही कारण है कि मैंने अन्य सभी पाठ को हटाने का फैसला किया। –

+0

मेरा प्रश्न यह है कि कार्य (LoopAsync) के कार्य में विशेष रूप से चार और कुछ नेटवर्क्सकॉलएसिंक कॉल के संदर्भ में क्या होता है क्योंकि पहले लूप के दौरान नियंत्रण को कॉलर पर वापस लाया जाता है। – Saravanan

1

जब async विधि पहले await पर उत्पन्न होती है, तो यह Task (या Task<T>) देता है। यह await द्वारा देखा गया कार्य है; यह async विधि द्वारा बनाई गई एक पूरी तरह से अलग कार्य है। async राज्य मशीन उस Task के जीवनकाल को नियंत्रित करती है।

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

तो, वहाँ लौटे कार्य करने के लिए निरंतरता संलग्न की आवश्यकता नहीं है। लौटाया गया कार्य पूरा होने तक पूरा नहीं होगा।

कैसे/किस तरह से, अगले कोड लाइनों जैसे गिनती ++ और आगे कुछ नेटवर्क्सकॉलएसिंक को निष्पादित किया जाएगा?

मैं इसे अपने async intro पोस्ट में समझाता हूं। सारांश में, जब एक विधि await है, यह एक "वर्तमान संदर्भ" (SynchronizationContext.Currentnull जब तक यह है, जिस स्थिति में यह TaskScheduler.Current उपयोग करता है) कैप्चर करता है। जब await पूर्ण हो जाता है, तो वह उस संदर्भ में async विधि निष्पादित करता है।

कि क्या तकनीकी रूप से होता है; लेकिन अधिकांश मामलों में, यह बस का अर्थ है: एक async विधि एक यूआई धागे पर शुरू होता है

  • है, तो यह है कि एक ही यूआई धागे पर फिर से शुरू होगा।
  • एक async विधि एक ASP.NET अनुरोध संदर्भ में शुरू होता है, तो यह है कि एक ही अनुरोध संदर्भ के साथ फिर से शुरू होगा (जरूरी नहीं एक ही धागा है, हालांकि पर)।
  • अन्यथा, async विधि एक धागा पूल धागे पर शुरू।
संबंधित मुद्दे