いまさらC#でasync/await

いまいち理解が定着してなかったのでおさらい。

  • asyncなメソッドからTaskを起動してawaitする。
  • awaitするといったんそのメソッドからリターンする。
  • タスクが完了するとそのメソッドに戻り、awaitの続きから処理される。
  • つまり、途中で中断/後で再開するのがasyncなメソッド。
  • async/awaitと対のように言うけど、awaitはasyncなメソッドの中で使うもの。
  • async/await使うとThreadを自分で書かなくて済む。
  • つまり糖衣構文にすぎないが、記述がブツ切りにならないので、書きやすく読みやすい。
  • まあ、それだけ。

簡単なサンプルコード

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    // asyncなメソッド
    private async void button1_Click(object sender, EventArgs e)
    {
        Console.WriteLine("button1_Click() begins : @Thread "
            + Thread.CurrentThread.ManagedThreadId.ToString());

        // 重い処理をどっかの別スレッドで実行 (おまかせ)
        Task<int> task = Task.Run<int>(new Func<int>(HeavyTask));
        Console.WriteLine("before await");

        // ここでいったん中断 / 重い処理の終わった後で再開
        int result = await task;
        
        Console.WriteLine("after await : @Thread "
             + Thread.CurrentThread.ManagedThreadId.ToString());
        this.textBox1.Text = result.ToString();
        Console.WriteLine("button1_Click() ends");
    }
    
    // 重い処理
    private int HeavyTask()
    {
        Console.WriteLine("HeavyTask() begins : @Thread "
            + Thread.CurrentThread.ManagedThreadId.ToString());

        int sum = 0;
        for (int i = 0; i <= 10; ++i)
        {
            sum += i; // まあ、この計算は重くないけど
        }
        Thread.Sleep(3000); // ここで3秒かかる (重い)
        Console.WriteLine("HeavyTask() ends");
        return sum;
    }
}

実行結果例

button1_Click() begins : @Thread 9
before await
HeavyTask() begins : @Thread 10
HeavyTask() ends
after await : @Thread 9
button1_Click() ends

注意事項

  • ほんとうは、asyncなメソッドはTaskを戻り値にしないといけない。
  • 上のサンプルコードはUIのイベントハンドラなので、例外的にasync voidで大丈夫。
  • UIのイベントハンドラの場合には、たぶん裏で誰かがあんじょうやってくれてる。しらんけど。

参考にしたページ

qiita.com