awaitableオブジェクトの作成


サーバ側でもクライアント側でも、非同期プログラミングは常に行われています.サーバ側では、パフォーマンスを向上させ、より多くの同時トランザクションをサポートするために、スレッドをブロックすることはできません.クライアントでは,ユーザの操作に常にタイムリーに応答できるようにUIスレッドもブロックできないため,非同期プログラミングが必要な場合が多い.しかし、非同期プログラミングは困難であり、シリアルのビジネスロジックは分散され、コードの隅々に散らばっている.
    .Netは,最初のbegin/end-invokeモード(APM)からイベントベースの非同期モード(EAP)まで,非同期プログラミングの難易度を低減しようと試みたが,これらの技術はasyncとawaitが現れるまで論理フローが乱される問題を解決しなかった.最新リリースのwin 8とwp 8では、マイクロソフトがアプリケーションの応答性を強調しているため、asyncとawaitの登場はちょうどその時だ.具体的には、この2つのキーワードをどのように使うか、MSDNなどの資料を参照してください.ここで説明したいのは、非同期呼び出し可能なオブジェクトをどのように構築するかです.もちろん、TaskおよびTaskは、次のような最も一般的なawait可能なオブジェクトです.
private async void ok_Click(object sender, EventArgs e)
{
     Console.WriteLine("     ,         :");

     //    
     int ret = await Task<int>.Run(() => {
                int total = 0;
                for (int i = 0; i < 1000; i++)
                {
                    total += i;
                }

                return total;
     });

     Console.WriteLine("    :{0}",ret);

}

コントロールのイベント応答に数秒遅延する場合は、次のようにします.
private async void ok_Click(object sender, EventArgs e)
{
     Console.WriteLine("    ,    UI  ");

     await Task.Delay(5000);

     Console.WriteLine("     ,     !");
}

上記と等価な解決策も簡単に考えられます.
private async void ok_Click(object sender, EventArgs e)
{
     Console.WriteLine("    ,    UI  ");

     await Task.Run( ()=>Thread.Sleep(5000) );

     Console.WriteLine("     ,     !");
}

ここで注意しなければなりませんTaskRunモードは単純なパッケージであり,新しいスレッドで非同期コードを実行する必要があるため,効率的なモードではない.次に、新しいスレッドを作成することなく、同じ遅延効果を実現する「非同期」関数を独自に構築します(これにより、応答性を向上させるだけでなく、パフォーマンスを向上させることができますが、これは別の物語です).これは通常、TaskCompletionSourceオブジェクトを使用します.
private async void ok_Click(object sender, EventArgs e)
{
    Console.WriteLine("    ,    UI  ");

    await MyDelay(5000);

    Console.WriteLine("     ,     !");
}

public Task MyDelay(int ms)
{
    TaskCompletionSource<bool> tcs = null;

    System.Threading.Timer timer = new System.Threading.Timer(
             (obj) => { //          
                         tcs.TrySetResult(true);
                      },
                   tcs,-1,-1
           );

     tcs = new TaskCompletionSource<bool>(timer);
     timer.Change(ms, -1);

     return tcs.Task;
}

TaskCompletionsourceは、非同期操作が終了するときに結果(TrySetResult)を設定するためにTaskオブジェクトを構築するためによく使用されます.しかしながら、Taskオブジェクトのみがawait可能であるわけではないが、実際には、GetAwaiter()メソッド(このメソッドが返すオブジェクトがINotifyCompletionインタフェースを実装する)を実装したオブジェクトは、いずれもawatableである.
では、int(Int 32)タイプはawaitできますか?もちろんいいです.intタイプにGetAwaiter()メソッドがあればいいです.
まず、非同期遅延を完了し、INotifyCompletionインタフェースを実装できるDelayAwaitクラスを実装します.
public class DelayAwait : INotifyCompletion
{
        private int delay;
        private bool finished;

        public DelayAwait(int ms)
        {
            this.delay = ms;
        }

        public bool IsCompleted
        {
            get { return finished; } 
        }

        public void OnCompleted(Action continuation)
        {
            new Timer(
                   (obj) => {
                       continuation();  //    ,  await     
                       finished = true;
                      },  
                   this,
                   delay, //    
                   -1                
                );
        }

        public void GetResult() { }
}
では、intタイプを「有」にします.
GetAwaiter()メソッド、これは簡単です.拡張メソッドの特性を利用すればいいです.
public static DelayAwait GetAwaiter(this int ms)
{
    return new DelayAwait(ms);
}
okです.これにより、非同期遅延機能を完了できます.
private async void ok_Click(object sender, EventArgs e)
{
    Console.WriteLine("    ,    UI  ");

    await 5000;  //  int       GetAwaiter(),   awaitable 

    Console.WriteLine("     ,     !");
}
DelayAwaitクラスの実装に注意する必要があります.INotifyCompletionはOnCompletedメソッドを実装する必要があることを規定していますが、実際にawaitをサポートするにはIsCompletedプロパティとGetResultメソッドも提供する必要があります.c#コンパイラはawaitのコードに出会って、背後でこっそり、自動的に1つのステータスマシンを生成して本当に“非同期”を実現して、私達は方法が本当に“中断”されると思ってはいけなくて、これはすべてコンパイラが発揮する魔法にすぎません.コンパイラが自動的に生成するコードで、呼び出されます.
IsCompleted、
OnCompleted、
GetResultこれらの方法は,我々もこれらの方法を実現しなければならない.簡単に言えば、先に判断します.
IsCompleted,偽であれば呼び出す
OnCompletedメソッドでは、非同期操作が完了したときに必要な後続の操作、すなわちawait後のコードを登録します.この背後にあるメカニズムを明らかにしたいなら、WinRTとawaitを深く探究することをお勧めします.
このように見ると、すべてがawaitableになります.
添付(参考資料):
     Async/Await FAQ