for WPF developers
非同期処理で重たい処理をおこなう場合に、 中断したくなるときがあります。 ここでは async/await 演算子による非同期処理を途中でキャンセルする例を紹介します。
サンプルプロジェクトはここからダウンロードできます。
ページ内リンク
まず UI の XAML コードとその実行結果を示します。 ボタンを押したら非同期処理で重たい処理を実行し、 キャンセルボタンでその処理を中断させるサンプルです。
非同期処理の記述は「async/await による非同期処理 その 1」で示したサンプルプログラムとほぼ同じですが、 一部だけ変更して中断処理を実現しようと思います。
まず、ViewModel のプロパティです。 Result プロパティで実行中もしくは実行終了を表し、 IsBusy プロパティで実行中を表現するところは同じです。 また、ButtonCommand プロパティによって非同期処理を開始するところも同じです。 最後に CancelCommand プロパティが追加されており、 private プロパティである CancellationTokenSource クラスの CancelTokenSource プロパティを使用しています。
非同期処理を途中で中断するには、CancellationTokenSource クラスを使用します。 非同期処理のほうで CancellationTokenSource クラスの持つ IsCancellationRequested プロパティを確認し、 これが true のときに処理を中断するようにします。 中断を指令する側は、上記のように Cancel() メソッドをコールするだけとなります。
非同期処理を開始する HeavyWorkAsync() メソッドを含む非同期関連のメソッドは次のようになります。
#region private メソッド
/// <summary>
/// 重たい処理を非同期で実行します。
/// </summary>
private async void HeavyWorkAsync()
{
IsBusy = true;
CancelTokenSource = new CancellationTokenSource();
Result = "只今実行中...";
System.Console.WriteLine("Thread[{0}] 非同期処理を実行します。", Thread.CurrentThread.ManagedThreadId);
await HeavyWorkTask();
// ここで一旦 return される
// 非同期処理が終了したらここから再開する
System.Console.WriteLine("Thread[{0}] 非同期処理を実行しました。", Thread.CurrentThread.ManagedThreadId);
if (CancelTokenSource.IsCancellationRequested)
{
Result = "キャンセルしました。";
}
else
{
Result = "終了しました。";
}
CancelTokenSource = null;
IsBusy = false;
}
/// <summary>
/// 重たい処理を実行するタスクを返します。
/// </summary>
/// <returns>重たい処理を実行するタスク</returns>
private Task HeavyWorkTask()
{
return Task.Run(() =>
{
// 重たい処理を実行
System.Console.WriteLine("Thread[{0}] 重たい処理を実行します。", Thread.CurrentThread.ManagedThreadId);
HeavyWork(CancelTokenSource != null ? CancelTokenSource.Token : new CancellationToken(false));
System.Console.WriteLine("Thread[{0}] 重たい処理を終了します。", Thread.CurrentThread.ManagedThreadId);
});
}
/// <summary>
/// 重たい処理を実行します。
/// </summary>
/// <param name="token">キャンセル処理をおこなうための <code>System.Threading.CancellationToken</code> クラスを指定します。</param>
private void HeavyWork(CancellationToken token)
{
int count = 0;
while (count++ < 100)
{
// キャンセルされたかどうか確認する
if (token.IsCancellationRequested)
{
System.Console.WriteLine("Thread[{0}] 重たい処理を中断します。", Thread.CurrentThread.ManagedThreadId);
return;
}
// 重たい処理
Thread.Sleep(30);
}
}
#endregion private メソッド
実行結果を次の図に示します。
Designed by CSS.Design Sample