1. ホーム
  2. c#

[解決済み] 複数のタスクにasync/awaitを使用する

2022-03-21 07:35:03

質問

完全に非同期のAPIクライアントを使っています。 Task または Task<T> は、例えば

static async Task DoSomething(int siteId, int postId, IBlogClient client)
{
    await client.DeletePost(siteId, postId); // call API client
    Console.WriteLine("Deleted post {0}.", siteId);
}

C# 5 の async/await 演算子を使用して、複数のタスクを開始し、それらのすべてが完了するのを待つための、正しい/最も効率的な方法は何ですか。

int[] ids = new[] { 1, 2, 3, 4, 5 };
Parallel.ForEach(ids, i => DoSomething(1, i, blogClient).Wait());

または

int[] ids = new[] { 1, 2, 3, 4, 5 };
Task.WaitAll(ids.Select(i => DoSomething(1, i, blogClient)).ToArray());

APIクライアントは内部でHttpClientを使っているので、すぐに5つのHTTPリクエストを発行し、それぞれのリクエストが完了するたびにコンソールに書き込むと思います。

どのように解決するのですか?

int[] ids = new[] { 1, 2, 3, 4, 5 };
Parallel.ForEach(ids, i => DoSomething(1, i, blogClient).Wait());

上記のコードで操作を並列に実行していますが、このコードでは各操作が実行されるスレッドをブロックしています。例えば、ネットワーク呼び出しに2秒かかるとすると、各スレッドは2秒間何もせずに待ちます。

int[] ids = new[] { 1, 2, 3, 4, 5 };
Task.WaitAll(ids.Select(i => DoSomething(1, i, blogClient)).ToArray());

一方、上記のコードに WaitAll もスレッドをブロックしてしまうので、その処理が終わるまでスレッドは他の処理をすることができません。

推奨するアプローチ

私は WhenAll を使えば、Parallelで非同期的に処理を実行することができます。

public async Task DoWork() {

    int[] ids = new[] { 1, 2, 3, 4, 5 };
    await Task.WhenAll(ids.Select(i => DoSomething(1, i, blogClient)));
}

実は、上記の場合にも await 連続性がないため、メソッドから直接リターンすることができます。

public Task DoWork() 
{
    int[] ids = new[] { 1, 2, 3, 4, 5 };
    return Task.WhenAll(ids.Select(i => DoSomething(1, i, blogClient)));
}

このことを裏付けるために、以下のブログ記事で、すべての そのメリット・デメリットをご紹介します。 ASP.NET Web APIによる非同期I/Oの並行処理の方法と場所