2013-12-13 5 views
13

非同期メソッドは待機中のスレッドをブロックしないためIO作業には適しているが、これは実際にどのように可能ですか?私は何かを完了するためにタスクをトリガするために聞いている必要がありますので、これはブロッキングがちょうど別の場所に移動されたことを意味ですか?async-awaitはどのようにブロックされませんか?

答えて

20

いいえ、ブロックは他の場所に移動されません。待ち状態の型を返すBCLメソッドは、I/O完了ポートとのオーバーラップI/Oなどの技術を使用して、完全に非同期のエクスペリエンスを実現します。

私はrecent blog postを持っています。これは、これがどのようにして物理的なデバイスまで戻ってきたかを説明しています。

+4

クール!私がこの質問を考えたとき、あなたのブログを実際に読んでいたのは面白いです。私は再びstackoverflowに行く前にすべてのあなたのポストを読む必要がありますように見えます! – NickL

+1

@NickL、あなたは一人ではありません。:) –

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

(ただし、厳密に)のようなものにしてなったが、それは基本的な主旨で取得しますそれの。それが存在していて、何かもっとこの

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

などはこれが正確に何が起こるかはまだありませんが、重要なことは奪うようにあれば、それが機能GetAwaiter()を呼び出し、本当に起きているのである


awaiter.IsCompletedがtrueの場合、すぐに戻るのではなく、ポストバックコードを同期して実行します。

クールなことは、あなたがいる限り、それは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() { ... } 
} 

に満たすことができるよう、あなたがタスクに待機する必要はありません、 await anythingことができています

making my wrong answer even more wrongの継続的な冒険では、コンパイラが私のexample関数をtoに変えた実際の逆コンパイルされたコードがここにあります。あなたがそこに目を通す場合

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

は、今あなたがasyncを使用する場合、コンパイラは、実際にIAsyncStateMachineインターフェイスに基づいてstate machineへのあなたの機能をオンためであるFoo()BarAsync()、またはBaz(barResult)への参照が存在しないでしょう。私たちが見に行く場合、コンパイラは新しい構造体が<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

@Servy oops、apologies。私は彼らが機能に言及していると思った。彼らは目立つように見えますが、それを強調するには何か良いことがありますか? – m59

関連する問題