LoginSignup
27
23

More than 5 years have passed since last update.

非同期で複数処理を実行し、対話式で制御する

Last updated at Posted at 2015-04-29

概要

開始と終了命令をコンソールからいつでも入力可能かつ、開始時には非同期で複数の処理を実行可能で、任意のタイミングでいつでも処理を停止できるようなプログラムを組んでみた

こんなイメージ
スクリーンショット 2015-04-29 20.08.13.png

非同期でタスクを実行

// ABCを非同期で同時実行
Task.Run(ActionA);
Task.Run(ActionB);
Task.Run(ActionC);

タスクを取り消す

タスクを取り消すにはMSのタスクとその子を取り消すにある通りCancellationTokenSourceを利用すると実装できるみたい。

// キャンセルトークンの取得
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;

// トークン付きで実行
Task.Run(()=>ActionA(token), token);

// 停止命令を送る
tokenSource.Cancel()

// ActionAに停止処理を追加
static void ActionA(CancellationToken token)
   {
      while(true){
         // メインロジック
         Thread.sleep(1000);

         // キャンセル時の処理
         if (token.IsCancellationRequested) {
              break;
         } 
      }
   } 

実際のコード

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

namespace AsyncPractice
{
    class Program
    {
        static void Main(string[] args)
        {
            // Task停止用のトークン発行
            var cancelTokenSource = new CancellationTokenSource();
            CancellationToken cToken = cancelTokenSource.Token;

            while (true)
            {

                // 入力受付
                Console.Write("> ");
                string msg = Console.ReadLine();

                // 処理開始
                if (msg == "stat" || msg == "s")
                {
                    Console.WriteLine("Ascnc Practice START");

                    // 非同期処理をそれぞれ実行
                    Task.Run(() => sleep("processA", 3000, cToken), cToken);
                    Task.Run(() => sleep("processB", 5000, cToken), cToken);
                    Task.Run(() => sleep("processC", 1000, cToken), cToken);

                    continue;
                }

                // 処理終了
                if (msg == "stop")
                {
                    // 非同期処理の終了
                    cancelTokenSource.Cancel();
                    Console.WriteLine("Stop!");
                }

                // アプリ終了
                if (msg == "quit" || msg == "q")
                {
                    break;
                }

            }
            // 非同期処理の終了
            cancelTokenSource.Cancel();
            cancelTokenSource.Dispose();
        }

        public static void sleep(string tag, int millseconds, CancellationToken token)
        {
            int ct = 0;
            while (true)
            {
                Thread.Sleep(millseconds);
                ct += 1;
                Console.WriteLine("[{0}] {1}回目のループ完了", tag, ct.ToString());

                // 中止リクエストあれば停止
                if (token.IsCancellationRequested)
                {
                    Console.WriteLine("[{0}] 処理停止", tag);
                    break;
                }
            }
        }

    }
}


実行してみた

スクリーンショット 2015-04-29 20.31.46.png

スクリーンショット 2015-04-29 20.32.18.png

実現できていなこと

・中断と再開の処理が上記コードでは実現できていない。
・CancellationTokenSourceは使い捨て推奨だが、このコードでは使い回している。disposeした後にtokenを再利用すると破棄されているためエラーが発生する。

対話式アプリではライフサイクル設計が必要

アプリ起動 → 処理起動 → 一時停止 → 再開 → 処理停止 → アプリ終了
のように対話式でステータスを変更するアプリを開発するには、ライフサイクル設計をする必要がある。作っていて「処理停止」と「アプリ停止」が異なる概念と気づきました。

27
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
23