(C#) 並行処理キー入力まちプログラム

参考サイト

mslgt.hatenablog.com

ポイント

CancellationTokenSource() でトークン作成して、トークンからキャンセル用トークンを生成して、それをすべての非同期タスクで共有して、キャンセルが起きた時の処理をタスクに書くってとこ。

今回は WaitKeyTask でしかキャンセルしてないけど、すべてのタスクで共有して、WaitKeyTaskともども Task.WaitAll() で待っておけば、どこからでもキャンセルできるようになると思う。

コード

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

namespace ExampleOfWaitKey
{
    class Program
    {
        static void Task1(CancellationToken token, string c, int duration)
        {
            while (true)
            {
                Console.Write(c);
                Task.Delay(duration).Wait();

                if(token.IsCancellationRequested)
                {
                    // WaitKeyTask() でキャンセルが発行されると
                    // Task.Delay().Wait() 明けにこちらに落ちる

                    // 終了に時間がかかるような処理の代わり
                    var t = new Random().Next(1,10);
                    Task.Delay(t * 100).Wait();

                    Console.WriteLine();
                    Console.WriteLine("{0}: was canceled", c);
                    return;
                }
            }
        }

        static void WaitKeyTask(CancellationTokenSource tokenSource)
        {
            Console.ReadLine(); // キー入力まち
            tokenSource.Cancel(); // キャンセル通知
        }

        static void Main(string[] args)
        {
            // キャンセル伝播用トークンの作成
            var tokenSource = new CancellationTokenSource();
            var cancelToken = tokenSource.Token;

            // キー入力待ち非同期タスクの実行
            var waitKeyTask = Task.Run(() =>WaitKeyTask(tokenSource));

            // 非同期処理タスクの実行
            // 
            var tasks = new List<Task>();
            tasks.Add(Task.Run(() => Task1(cancelToken, "a", 100)));
            tasks.Add(Task.Run(() => Task1(cancelToken, "b", 200)));

            // キー入力待ち
            waitKeyTask.Wait();

            Console.WriteLine();
            Console.WriteLine("キーが押されました。");
            Console.WriteLine("すべてのタスクが終了するのを待ちます...");

            // 全タスクのキャンセル処理待ち
            Task.WaitAll(tasks.ToArray());
        }
    }
}