2013-12-13 23 views
13

मैं इकट्ठा करता हूं कि आईआईएन काम के लिए एसिंक विधियां अच्छी हैं क्योंकि वे थ्रेड को अवरुद्ध नहीं करते हैं, लेकिन यह वास्तव में कैसे संभव है? मुझे लगता है कि कार्य को पूरा करने के लिए कुछ ट्रिगर करना सुनना है, तो क्या इसका मतलब यह है कि अवरोध कहीं और स्थानांतरित हो गया है?async-await ब्लॉक क्यों नहीं करता है?

उत्तर

20

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

मेरे पास recent blog post है जो वर्णन करता है कि यह भौतिक डिवाइस और पीछे कैसे काम करता है।

+4

कूल करना था! यह मजाकिया है कि जब मैं इस सवाल के बारे में सोचा था तो मैं वास्तव में आपके ब्लॉग को पढ़ रहा था। ऐसा लगता है कि मैं स्टैक ओवरफ्लो पर फिर से जाने से पहले आपको अपने सभी पोस्ट पढ़ना होगा! – NickL

+1

@ निकल, आप अकेले नहीं हैं।:) –

11

Async-await वास्तव में आपके लिए अपना कोड फिर से लिख रहा है। यह क्या करता है कार्य कार्य निरंतरता का उपयोग करता है और निरंतरता को सिंक्रनाइज़ेशन संदर्भ पर वापस रखता है जो निरंतरता उत्पन्न होने पर चालू था।

तो निम्नलिखित समारोह

public async Task Example() 
{ 
    Foo(); 
    string barResult = await BarAsync(); 
    Baz(barResult); 
} 

की तरह (लेकिन वास्तव में) इस

public Task Example() 
{ 
    Foo(); 
    var syncContext = SyncronizationContext.Current; 
    return BarAsync().ContinueWith((continuation) => 
        { 
         Action postback =() => 
         { 
          string barResult = continuation.Result(); 
          Baz(barResult) 
         } 

         if(syncContext != null) 
          syncContext.Post(postback, null); 
         else 
          Task.Run(postback); 
        }); 
} 
अब

कि तुलना में अपने वास्तव में एक बहुत अधिक जटिल कुछ करने के लिए बदल गया है लेकिन है कि बुनियादी सार है इसका


क्या वास्तव में हो रहा है यह है कि यह समारोह GetAwaiter() कॉल करता है, तो यह मौजूद है और अधिक इस

public Task Example() 
{ 
    Foo(); 
    var task = BarAsync(); 
    var awaiter = task.GetAwaiter(); 

    Action postback =() => 
    { 
     string barResult = awaiter.GetResult(); 
     Baz(barResult) 
    } 


    if(awaiter.IsCompleted) 
     postback(); 
    else 
    { 
     var castAwaiter = awaiter as ICriticalNotifyCompletion; 
     if(castAwaiter != null) 
     { 
      castAwaiter.UnsafeOnCompleted(postback); 
     } 
     else 
     { 
      var context = SynchronizationContext.Current; 

      if (context == null) 
       context = new SynchronizationContext(); 

      var contextCopy = context.CreateCopy(); 

      awaiter.OnCompleted(() => contextCopy.Post(postback, null)); 
     } 
    } 
    return task; 
} 

की तरह कुछ यह अभी भी नहीं वास्तव में क्या होता है करता है, लेकिन महत्वपूर्ण बात यह है लेने के लिए यदि awaiter.IsCompleted सत्य है, तो यह तुरंत वापस लौटने की बजाय सिंक्रनाइज़ रूप से पोस्टबैक कोड चलाएगा।

अच्छी बात यह है, तो आप एक टास्क पर इंतजार करने की जरूरत नहीं कर सकते हैं await anything जब तक कि यह एक समारोह GetAwaiter() कहा जाता है और वापस आ वस्तु निम्नलिखित हस्ताक्षर

public class MyAwaiter<TResult> : INotifyCompletion 
{ 
    public bool IsCompleted { get { ... } } 
    public void OnCompleted(Action continuation) { ... } 
    public TResult GetResult() { ... } 
} 
//or 
public class MyAwaiter : INotifyCompletion 
{ 
    public bool IsCompleted { get { ... } } 
    public void OnCompleted(Action continuation) { ... } 
    public void GetResult() { ... } 
} 

पूरा कर सकते हैं के रूप में है, तो आप

making my wrong answer even more wrong पर जारी साहसिक पर, यहां वास्तविक डिकंपील्ड कोड है जो संकलक मेरा उदाहरण फ़ंक्शन में बदल जाता है।

[DebuggerStepThrough, AsyncStateMachine(typeof(Form1.<Example>d__0))] 
public Task Example() 
{ 
    Form1.<Example>d__0 <Example>d__; 
    <Example>d__.<>4__this = this; 
    <Example>d__.<>t__builder = AsyncTaskMethodBuilder.Create(); 
    <Example>d__.<>1__state = -1; 
    AsyncTaskMethodBuilder <>t__builder = <Example>d__.<>t__builder; 
    <>t__builder.Start<Form1.<Example>d__0>(ref <Example>d__); 
    return <Example>d__.<>t__builder.Task; 
} 

अब अगर आप वहाँ के माध्यम से देखने के लिए आप Foo(), BarAsync(), या Baz(barResult) के लिए कोई संदर्भ यह है, क्योंकि जब आप async का उपयोग संकलक वास्तव में एक state machineIAsyncStateMachine इंटरफ़ेस के आधार पर करने के लिए अपने समारोह बदल जाता है देखेंगे। हम देखने जाते हैं, संकलक एक नया struct <Example>d__0

[CompilerGenerated] 
[StructLayout(LayoutKind.Auto)] 
private struct <Example>d__0 : IAsyncStateMachine 
{ 
    public int <>1__state; 
    public AsyncTaskMethodBuilder <>t__builder; 
    public Form1 <>4__this; 
    public string <barResult>5__1; 
    private TaskAwaiter<string> <>u__$awaiter2; 
    private object <>t__stack; 
    void IAsyncStateMachine.MoveNext() 
    { 
     try 
     { 
      int num = this.<>1__state; 
      if (num != -3) 
      { 
       TaskAwaiter<string> taskAwaiter; 
       if (num != 0) 
       { 
        this.<>4__this.Foo(); 
        taskAwaiter = this.<>4__this.BarAsync().GetAwaiter(); 
        if (!taskAwaiter.IsCompleted) 
        { 
         this.<>1__state = 0; 
         this.<>u__$awaiter2 = taskAwaiter; 
         this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, Form1.<Example>d__0>(ref taskAwaiter, ref this); 
         return; 
        } 
       } 
       else 
       { 
        taskAwaiter = this.<>u__$awaiter2; 
        this.<>u__$awaiter2 = default(TaskAwaiter<string>); 
        this.<>1__state = -1; 
       } 
       string arg_92_0 = taskAwaiter.GetResult(); 
       taskAwaiter = default(TaskAwaiter<string>); 
       string text = arg_92_0; 
       this.<barResult>5__1 = text; 
       this.<>4__this.Baz(this.<barResult>5__1); 
      } 
     } 
     catch (Exception exception) 
     { 
      this.<>1__state = -2; 
      this.<>t__builder.SetException(exception); 
      return; 
     } 
     this.<>1__state = -2; 
     this.<>t__builder.SetResult(); 
    } 
    [DebuggerHidden] 
    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0) 
    { 
     this.<>t__builder.SetStateMachine(param0); 
    } 
} 

धन्यवाद लोगों को उनकी उपकरण एक पुस्तकालय है कि आप का विस्तार करने और कोड से अपने आप को कॉल कर सकते हैं का उपयोग करने के लिए खत्म हो गया कहा जाता ILSpy में उत्पन्न। उपर्युक्त कोड प्राप्त करने के लिए मुझे केवल

using System.IO; 
using ICSharpCode.Decompiler; 
using ICSharpCode.Decompiler.Ast; 
using Mono.Cecil; 

namespace Sandbox_Console 
{ 
    internal class Program 
    { 
     public static void Main() 
     { 
      AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(@"C:\Code\Sandbox Form\SandboxForm\bin\Debug\SandboxForm.exe"); 
      var context = new DecompilerContext(assembly.MainModule); 
      context.Settings.AsyncAwait = false; //If you don't do this it will show the original code with the "await" keyword and hide the state machine. 
      AstBuilder decompiler = new AstBuilder(context); 
      decompiler.AddAssembly(assembly); 

      using (var output = new StreamWriter("Output.cs")) 
      { 
       decompiler.GenerateCode(new PlainTextOutput(output)); 
      } 
     } 
    } 
} 
+0

"ठीक है, तो इसका मतलब है कि अवरोध BarAsync() में किया जाता है।" मुझे पता है कि आप सही हैं, लेकिन आपका जवाब यह नहीं बताता है कि क्यों स्टीफन क्लेरी करता है। – hvd

+0

@ m59 कोड कोड में डाले गए कुछ भी चीजें कोड के वास्तविक ब्लॉक हैं ... – Servy

+0

@ सर्वी ओप्स, क्षमा करें। मैंने सोचा कि वे एक समारोह का जिक्र कर रहे थे। ऐसा लगता है कि उन्हें खड़ा होना चाहिए ... क्या इसके साथ हाइलाइट करने के लिए कुछ बेहतर है? – m59

संबंधित मुद्दे