2013-12-13 17 views
13

Async yöntemlerinin IO çalışması için iyi olduğunu anlıyorum çünkü bekledikleri sırada iş parçacığını engellemiyorlar, ancak bu nasıl mümkün olabilir? Tamamlama görevini tetiklemek için bir şeyler dinlemeyi düşünürüm, bu da engellemenin başka bir yere taşındığı anlamına mı geliyor?Uyumsuzluk nasıl engellenmez?

cevap

20

Hayır, engelleme başka hiçbir yere taşınmaz. Beklenmeyen türler döndüren BCL yöntemleri, tam eşzamansız bir deneyim için G/Ç tamamlama portlarıyla çakışan G/Ç gibi teknikleri kullanır.

Bunun, fiziksel aygıtla ve geriye doğru nasıl çalıştığını açıklayan bir recent blog post var.

+4

Cool! Bu soruyu düşündüğümde gerçekten blog'unu okuyordum. Ben tekrar stackoverflow gitmeden önce TÜM POSTLARINIZI okumak zorunda gibi görünüyor! – NickL

+1

@NickL, yalnız değilsiniz.:) –

11

Async-await aslında sizin için kodunuzu yeniden yazıyor. Yaptığı şey bir Görev Devam Etme kullanmaktır ve bu sürekliliği, devam edildiğinde geçerli olan Senkronizasyon bağlamına geri koyar.

Yani şu fonksiyon

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

Şimdi (ama tam olarak) bu

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); 
        }); 
} 

daha onun aslında çok daha karmaşık gibi bir şeye döndü Gets the

, ama bu temel esası olan onun. Var olduğundan ve bir şey daha bu

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; 
} 

gibi bu tam olarak ne olduğunu hala yapar ama önemli olan is götürmek için eğer buna işlevini GetAwaiter() dediği şey gerçekten oluyor olup


awaiter.IsCompleted doğruysa, hemen geri gönderilmek yerine geri gönderme kodunu eşzamanlı olarak çalıştıracaktır.

serin şey sürece GetAwaiter() adında bir işlevi vardır ve döndürülen nesne aşağıdaki imzayı

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() { ... } 
} 

yerine getirebilir olarak, bir Görev üzerine bekliyor gerekmez, await anything olabilir olduğunu

Devam etmekte olan making my wrong answer even more wrong serüveninde, derleyicinin örnek işlevimi değiştirdiği asıl derlenmiş kod buradadır. Orada bakmak durumunda

[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; 
} 

Şimdi de async kullandığınızda derleyici aslında IAsyncStateMachine arayüzüne dayalı bir state machine için Işlevinizin döner çünkü bu Foo(), BarAsync() veya Baz(barResult) için bir atıf yoktur göreceksiniz. biz bakmak giderseniz, derleyici yeni yapı kendi aracı genişletmek ve koddan kendinizi çağırabilir bir kütüphane kullanmak yapmak için ILSpy insanlara <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); 
    } 
} 

Teşekkür üzerinde adlandırılan üretti. Yukarıdaki kodu almak için tek yapmam gereken,

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

"Tamam, bu sadece engellemenin BarAsync() içinde yapıldığı anlamına geliyor." Haklı olduğunu biliyorum, ama cevabın neden açıklanmıyor, Stephen Cleary'nin yaptığı gibi. – hvd

+0

@ m59 Kod bloklarına koyduğunuz şeylerin hiçbiri gerçek kod blokları değildir ... – Servy

+0

@Servy oops, özür dileriz. Bir işleve başvurduklarını sanıyordum. Hala göze çarpmaları gerektiği gibi görünüyor ... bununla ilgili daha iyi bir şey var mı? – m59

İlgili konular