読者です 読者をやめる 読者になる 読者になる

Xamarin 日本語情報

Xamarin(ザマリン) の代理店だったエクセルソフト田淵のブログです。主に Xamarin に関するエントリーをアップしていきます。(なるべく正しい有益な情報を掲載していきたいと考えていますが、このブログのエントリーは所属組織の公式見解ではありませんのでご注意ください)

Task 並列処理のメモ

こんにちは。エクセルソフトの田淵です。

勉強がてら、複数のサイトから Json を引っ張ってきて ListView に表示するだけのアプリを作っているのですが、以下のようなイメージで await, await, await してたら当然遅いわけです。

(Xamarin は勉強や調査で C# のコンソールアプリや WPF でかなりお手軽に試せるので初心者には嬉しいですね。)

private async void button1_Click(object sender, RoutedEventArgs e)
{
    var sw = new Stopwatch();
    sw.Reset();
    sw.Start();

    string str1 = await SomeTaskAsync1("foo");
    string str2 = await SomeTaskAsync("bar");
    string str3 = await SomeTaskAsync("baz");

    sw.Stop();
    label.Content = string.Format("sw.Elapsed: {0:ss}.{0:ff} sec\n{1}\n{2}\n{3}", sw.Elapsed, str1, str2, str3);
}

直接 Async メソッドを await するのではなく、Task<TResult> にしてから (2015/7/20 追記) Task.WhenAll(Task) で同時に実行すると並列処理が出来るようです。Task は難しくて全然分かりません。。

渋木さん、平野さん、コメントありがとうございます。

参考:方法: Task.WhenAll を使用してチュートリアルを拡張する (C# および Visual Basic)

await すると自動で並列処理してくれるようなので忘れないようにメモしておきます。

private async void button2_Click(object sender, RoutedEventArgs e)
{
    var sw = new Stopwatch();
    sw.Reset();
    sw.Start();

    // 多分こうやる。 (2015/7/20 追記)
    Task<string> task1 = SomeTaskAsync("foo");
    Task<string> task2 = SomeTaskAsync("bar");
    Task<string> task3 = SomeTaskAsync("baz");

    IEnumerable<Task<string>> tasks = new[] { task1, task2, task3 };
    // WhenAll するまで "task1    Id = 97, Status = WaitingForActivation, Method = "{null}", Result = "{未計算}" と出ているので多分遅延実行というやつなんだと思います。
    string[] results = await Task.WhenAll(tasks);

    // これは良くないやり方
    // var str1 = await task1;
    // var str2 = await task2;
    // var str3 = await task3;

    sw.Stop();
    label.Content = string.Format("sw.Elapsed: {0:ss}.{0:ff} sec\n{1}\n{2}\n{3}", sw.Elapsed, results[0], results[1], results[2]);
}

GW 中、Parallel.Invoke とか Queue<T> とか調べて悩んでたんですけどウチの技術に聞いたらすぐに 方法: Async と Await を使用して複数の Web 要求を並列実行する (C# および Visual Basic) を教えてくれました笑

助かりますな…

(ところで var str1 = await GetTaskAsync("task1"); と await すると var は TResult の string になって、await しないと var が Task になるんですね。当たり前なんですけど。。)

GetTaskAsync はサンプルとしてランダムに Delay するだけの単純なやつです。

public async Task<string> SomeTaskAsync(string arg)
{
    var rnd = new Random();
    var randomInt = rnd.Next(1, 10);

    await Task.Delay(randomInt * 100);
    return string.Format("{0}: {1}ms", arg, randomInt * 100);
}

これで早くなるといいなー。

以上です。