いっつも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)を作っている。
挫折を何回も繰り返し、幾度の壁を乗り越えてきた。
乗り越えてきた事を忘れないように記録に残す。
同じ思いをしている人への情報提供になれたらと思う。
基本は初心者に向けたプログラムの情報を提供する。



コメント