2016年9月11日日曜日

GUIでの非同期処理(async/await)

先に今日の内容をまとめておきます。

非同期処理のAsync/Awaitは便利です。


では、前ふりです。

GUIでの非同期処理って、いろいろと面倒なイメージがありませんか。
非同期処理を行う場合って、脳内ではこんなこと考えていませんでしたか?

ボタンを押す→処理が重たい→スレッドにする→最終結果を表示したいから、スレッド終了待ち→待ってるとGUI固まる→DoEventでごまかす。

結局、スレッドにする、しないにかかわらず、クリック後の処理で待ちたいけれど、固まったように見えないようにしなければ!ってことを考えていたのではないでしょうか。

まずは、今まで書いていたようなコードを見てみましょう。

        private async void button1_Click( object sender, EventArgs e )
        {
            var msg = HeavyCalcurate_Thread();
            MessageBox.Show( msg );
        }

        // Asnc,Awaitがないころのコード(スレッドで回避)
        // 処理中でも、GUIを操作可能
        private String HeavyCalcurate_Thread()
        {
            var task = new TaskFactory().StartNew( ()=>
            {
                for( var i = 0; i < 10; i++ )
                {
                    System.Threading.Thread.Sleep( 2000 );
                }

            } );
            // 終了待ち
            var wait = false;
            while( wait == false )
            {
                wait = task.Wait( 100 );
                Application.DoEvents();//<---がんばって処理を逃がすw
            }
            return "完了";
        }


要は、スレッドの処理待ちです。スレッドが終わった後に、メッセージボックスを出したい。画面は、操作したい。って場合です。
では、C#5 で追加されたasync/awaitを使って書いてます。

private async void button1_Click( object sender, EventArgs e )
{
    var msg = await HeavyCalcurate_Async();
    MessageBox.Show( msg );
}
private async Task<String> HeavyCalcurate_Async()
{
    var task = await new TaskFactory().StartNew( ()=>
    {
        for( var i = 0; i < 10; i++ )
        {
            System.Threading.Thread.Sleep( 2000 );
        }
        return "完了";
    } );
    return task;
}

コードが簡単になっていますね。
上に書いた処理待ちのコードが、だいぶすっきりしています。
このように書くだけで、Taskが実行され、そのあとにちゃんとメッセージボックスが出ます。
さらに、一番大事なのは、処理待ちの間も問題なくGUIを動かしたりできます。

ってわけで、1つ賢くなったのですが、一つ残念なことがありました。
C# 5が当たり前な世代では、async/awaitがない場合にはなんて考えないかも?!
使うのが当たり前でしょって思ったそこのあなた!
どうか、筆者を年寄り扱いしないでくださいね。そこのところ、よろしくお願いいたします。

おまけ


コンパイラによるラムダ式の展開に絡みますが、上記コードをコンパイルしたコードを見てみると、
DebugとReleaseでだいぶコードが異なるので面白いかも。

0 件のコメント:

コメントを投稿