いっつもThread(スレッド)処理でやってるんだけど、参考にしているソフトがTask(タスク)を使っていたので、いい機会だと思いちょっと作ってみました。
いつも作っているThread処理
いつもスレッド処理を作る時は、2つのThreadを使っています。
1つは、実行処理用。もう一つは、終了処理用です。
イメージとしては下記の感じ。
このイメージでスレッドを作ると下記の感じになる。
using System.Threading; namespace Test1 { public partial class Form1 : Form { private Thread ThreadA; private Thread ThreadB; public Form1() { InitializeComponent(); ThreadA = new Thread(FunctionExecute); ThreadB = new Thread(FunctionComplete); ThreadA.Start(); ThreadB.Start(); } private void FunctionExecute() { Console.WriteLine("処理開始"); for (int n = 0;n < 10;n++) { Thread.Sleep(1000); Console.WriteLine((n + 1).ToString() + "秒"); } Console.WriteLine("処理終了"); } private void FunctionComplete() { do { Thread.Sleep(100); } while (ThreadA != null && ThreadA.IsAlive == true); Console.WriteLine("終了処理開始"); //--- 終了処理 --- Console.WriteLine("終了処理終了"); } } }
出力ウィンドウで結果を確認すると、下記の感じになる。
いい感じにできました。
いつものThread処理をTask処理に置き換えた時
ここから本題のTask処理に変更した場合です。
こんな風になりました。
using System.Threading.Tasks; namespace Test1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); // タスク生成・実行(ContinueWithはタスクが終わった後の処理) Task.Run(FunctionExecute()).ContinueWith(t => FunctionCompleteTask()); } private void FunctionExecute() { Console.WriteLine("処理開始"); for (int n = 0;n < 10;n++) { Thread.Sleep(1000); Console.WriteLine((n + 1).ToString() + "秒"); } Console.WriteLine("処理終了"); } private void FunctionCompleteTask() { Console.WriteLine("終了処理開始"); //--- 終了処理 --- Console.WriteLine("終了処理終了"); }
なんとThreadの時に比べて、数行減らす事が出来ました。
タスクの実行がちょっと特殊な書き方なのでわかりずらいっっ!!
簡単に説明すると、
●Task.Run(FunctionExecute()) タスクで、FunctionExecuteを実行するよ! ●ContinueWith(t => FunctionCompleteTask()) タスクが終わったら、FunctionCompleteTaskを実行してね!
1行で書くと分かりずらい場合は、2行に分けて書きましょう!!
// タスク生成・実行(ContinueWithはタスクが終わった後の処理) Task tmpTask = Task.Run(FunctionExecute()); tmpTask.ContinueWith(t => FunctionCompleteTask());
2行に分けて書くと分かりやすいですね。
ContinueWithの引数がAction<Task>で、書き方がこれ以外の書き方が分かりませんでした。
結果は先ほどと変わらないので割愛します。
これで無事にThreadからTaskへの切り替えが完了でした。
イメージ的にはこんな感じになりました。
ソースコードもスッキリしていい感じです。
Taskのキャンセル処理について
キャンセル要求という機能としてあるので、紹介します。
※個人的にはフラグ処理でもいいと思うのですが。。。笑
やりたい事は、下図の通りで、何かのタイミングでCancel要求を出して、Cancel要求を確認したらTaskを終了するという流れになります。
こんな感じのフォームを作ってみました。
まずは、CancellationTokenSouceを定義します。
namespace Test1 { public partial class Form1 : Form { private CancellationTokenSource TaskCanceler; (以下、後記)
先ほどの実行処理にCancel要求があった場合を追加します。
private void FunctionExecute() { Console.WriteLine("処理開始"); for (int n = 0;n < 10;n++) { Thread.Sleep(1000); Console.WriteLine((n + 1).ToString() + "秒"); if (TaskCanceler.Token.IsCancellationRequested) { Console.WriteLine("Cancelされました"); break; } } Console.WriteLine("処理終了"); }
次に終章処理では、CancellationTokenSouceを開放します。
private void FunctionCompleteTask() { Console.WriteLine("終了処理開始"); //--- 終了処理 --- // キャンセルクラスを解放 if (TaskCanceler != null) { TaskCanceler.Dispose(); TaskCanceler = null; } Console.WriteLine("終了処理終了"); }
あとは、タスクの実行ボタン(button1)の処理。
private void button1_Click(object sender, EventArgs e)
{
// タスクオブジェがnullなら生成
if (TaskCanceler == null) TaskCanceler = new CancellationTokenSource();
// タスク生成・実行(ContinueWithはタスクが終わった後の処理)
Task tmpTask = Task.Run(FunctionExecute).ContinueWith(t => FunctionCompleteTask());
}
最後にCancel要求ボタン(button2)の処理。
private void button2_Click(object sender, EventArgs e)
{
if(TaskCanceler != null) TaskCanceler.Cancel();
}
これを実行してみたところ。。。
無事にCancel要求を実施して、タスクを終了させることが出来ました。
まとめ
今回はThreadからの卒業の為にTaskについて記載しました。
調べているとごちゃごちゃ書いていて、正直わかりずらかったのを自分なりにまとめました。
自分がよくやっている【実行処理と終了処理】を分けて実施するのも、Taskではスマートにかけて、ちょっと「なんで早くチャレンジしなかった!!」と後悔です。。。
業務でプログラミング(C#/VB/Python)を作っている。
挫折を何回も繰り返し、幾度の壁を乗り越えてきた。
乗り越えてきた事を忘れないように記録に残す。
同じ思いをしている人への情報提供になれたらと思う。
基本は初心者に向けたプログラムの情報を提供する。
コメント