【C#】タスクの実行とキャンセル、バックグラウンドからのUI更新
並列処理というか非同期処理って重要ですよね。
そこで今回はUI更新を伴うトグルボタン的なものを書いてみました。
■概要
①ボタンを押すと、ボタンテキストの更新を開始します
②ボタンテキストにはTask内のループカウンタを表示します
③テキスト更新中(Task実行中)にボタンを押すと、ボタンテキストの更新(Task)を停止します
■ポイント
①CancellationTokenSource (Taskのキャンセルに利用)
②SynchronizationContext (Task実行中にUIを更新するために利用)
privateint counter_ =0;
privateTask task_ =null;
privateCancellationTokenSource tokenSource_ =null;
// タスク終了時に実行する処理
private async TaskEndTask(int n)
{
Debug.WriteLine("EndTask: counter = {0}", n);
}
// タスクの実行・停止を行うボタンのハンドラ
private async void StartButton_Click_1(object sender,RoutedEventArgs e)
{
if(task_ !=null)
{
tokenSource_.Cancel();
task_ =null;
await EndTask(counter_);
}
else
{
var context =SynchronizationContext.Current;// バックグラウンドからUIの更新要求を行うためのContext取得
tokenSource_ =newCancellationTokenSource();// 外部からTaskへキャンセル要求を発行するために必要
// タスクの開始
task_ =Task.Factory.StartNew(
async (_)=>
{
counter_ =0;
while(true)
{
// キャンセル要求の確認
if(tokenSource_.IsCancellationRequested)
{
context.Post(state =>
{
StartButton.Text="キャンセルされたよ!";
},
null);
}
// Taskのキャンセル要求を検出したとき、例外を生成してTaskを終了する
tokenSource_.Token.ThrowIfCancellationRequested();
// SynchronizationContextを利用したUI更新
context.Post(state =>
{
StartButton.Text= counter_.ToString();
},
null);
counter_++;
// 表示テキストの変更を目視できるようにループ間隔を制御
await Task.Delay(TimeSpan.FromMilliseconds(30));
}
}
, tokenSource_.Token// このタスクをキャンセルするCancellationToken
);
}
}