概要

あと少しでようやく終わる……。

 

流し読みノート

何がええねん非同期

  • Windows の機能の多くは非同期的に動かせるのに、多くの場合は同期 API 経由で使われている。
  • 同期 API で使うと、処理が完了するまでスレッドがブロックされてるのでムダなのだ。
  • 言語レベルの非同期サポートは C# 5.0 から。
    • てことはつまり、非同期処理を書くこと自体は昔からできたのだ。
// async await を使わないで非同期処理を書く。
// ヤバすぎ。
using System.Linq;

private void Foo(string url)
{
    var w = new System.Net.Http.HttpClient();
    var req = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Head, url);
    var uiScheduler = System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext();
    w.SendAsync(req, System.Net.Http.HttpCompletionOption.ResponseHeadersRead).ContinueWith(sendTask =>
    {
        try
        {
            System.Net.Http.HttpResponseMessage response = sendTask.Result;
            var headerString = from header in response.Headers
                               select header.Key + ":" + string.Join(",", header.Value);
            string headerList = string.Join(System.Environment.NewLine, headerString);
        }
        finally
        {
            w.Dispose();
        }
    }, uiScheduler);
}

 

async await を使うとどうなんねん

こうなんねん

using System.Linq;

private async void Bar(string url)
{
    using var w = new System.Net.Http.HttpClient();
    var req = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Head, url);
    System.Net.Http.HttpResponseMessage response =
        await w.SendAsync(req, System.Net.Http.HttpCompletionOption.ResponseHeadersRead);
    var headerString = from header in response.Headers
                       select header.Key + ":" + string.Join(",", header.Value);
    string headerList = string.Join(System.Environment.NewLine, headerString);
}
  • async await を使うと、コードが単純に書けて、非同期コードによる恩恵を簡単に得られる。
  • async はコードのコンパイル方法を決める。
  • ↑のコードを見ると、フツーのコード(同期コード)と同じじゃんと思うけれど、 await ついているとスレッドをブロックしないのだ。
// ブロックする場合はこう。
System.Net.Http.HttpResponseMessage response =
    w.SendAsync(req, System.Net.Http.HttpCompletionOption.ResponseHeadersRead).Result;

async await のうんちく

  • await の行まで実行が進むと、「実行コンテキストがキャプチャ」される(よくわからん)。
    • 同じメソッド内で複数 await があるときは、ひとつのコンテキストを使い回す。
  • async メソッドの返り値は void Task Task<T> のみ。
    • 基本的には Task を返すべき。例外を補足できたり、よいことばかり。 Task を返すことで不便になることはほぼない。
  • 匿名メソッドも非同期にできる。
async delegate(object s, RoutedEventArgs e)
{
    using (var w = HttpClient())
    {
        infoTextBlock.Text = await w.GetStringAsync(uriTextBox.Text);
    }
}
  • 非同期 API の多くは TPL(Task Parallel Library)タスクを返す。
  • もちろん例外処理もできるよ。
try
{
    string longest = await FindLongestLineAsync("http://localhost");
    Console.WriteLine(longest);
}
catch (HttpRequestException x)
{
    Console.WriteLine(x.Message);
}
  • async メソッドの引数を検証するときはラッパーメソッドが要る。
public Task<string> FindLongestLineAsync(string url)
{
    if (url == null)
    {
        throw new ArgumentNullException("url");
    }
    return FindLongestLineCore(url);
}

private async Task<string> FindLongestLineCore(string url)
{
    // ...
}

 

小休憩

この話は Pythonista の末席を汚しまくる者としてはわりと関心がある。実は Python にも async await があるのよな。たしか3.5あたりから。