2020-01-07 更新

タスク(Task.Run)で非同期処理の実行と待機する方法

これまでにC#で非同期処理を作成する機会が何度かあったのですが、やる度に間隔が空きすぎて毎回調べてしまっていたのでメモとして残しておきます。

目次

  • コード
  • 実行
  • 参考リンク

コード

下記のコードは、コンソールアプリケーションとしてプロジェクトを作成し、複数のタスクを並列で実行しつつ、最終的にすべてのタスク処理が完了するのを待ってから、処理を終了する方法です。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("開始");
            var task = ExampleAsync();
            task.Wait();
            Console.WriteLine("終了");
        }

        private static async Task ExampleAsync()
        {
            var list = new List<ExampleData>()
            {
                new ExampleData {Id = 1, Name = "AAA" },
                new ExampleData {Id = 2, Name = "BBB" },
                new ExampleData {Id = 3, Name = "CCC" }
            };

            // ForEach、WaitAll版
            var tasks01 = new List<Task>();
            list.ForEach(x =>
            {
                tasks01.Add(Task.Run(() => ExampleExecute(x)));
            });
            Task.WaitAll(tasks01.ToArray());

            // Select、WaitAll版
            var tasks02 = list.Select(x => Task.Run(() => ExampleExecute(x))).ToList();
            Task.WaitAll(tasks02.ToArray());

            // Select、WhenAll()版
            var tasks03 = list.Select(x => Task.Run(() => ExampleExecute(x))).ToList();
            var task03 = Task.WhenAll(tasks03);
            task03.Wait();

            // awaitでを順次実行(一つ一つのタスクが終了するのを待つ)
            await Task.Run(() => ExampleExecute(list[0]));
            await Task.Run(() => ExampleExecute(list[1]));
            await Task.Run(() => ExampleExecute(list[2]));
        }

        private static void ExampleExecute(ExampleData data)
        {
            System.Threading.Thread.Sleep(data.Id * 1000);
            Console.WriteLine(data.Name);
        }
    }

    public class ExampleData
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

メインのタスクと呼び出されたタスクが別物(非同期実行)となるので、Main()内の「task.Wait();」がないとメインタスクが開始と同時に終了します。

Task.Run()でWaitAll()するやり方は分かりやすいのですが、asyncやawaitの挙動が分からずにかなり悩みました。

WaitAll()とWhenAll()は引数と戻り値が違います。

実行

上記のコードを実行すると、最終的に下記のような結果がコンソールに出力されます。


開始
AAA
BBB
CCC
AAA
BBB
CCC
AAA
BBB
CCC
AAA
BBB
CCC
終了

参考リンク

C#】関連記事