> 作業効率UP!! オススメのモバイルモニターを紹介

【C#】非同期処理をキャンセルする方法(CancellationToken)

当ページのリンクには広告が含まれています。
  • URLをコピーしました!

この記事では非同期処理のキャンセル処理について紹介しています。

C# で非同期処理(async/await)を使用する際、気を付けなければいけないことの1つに「キャンセル処理」があります。

非同期処理では適切なキャンセル処理を実装しないと、不要な処理が実行されて CPU やメモリを消費し続ける可能性があります。また、デッドロックやデータ不整合を引き起こす可能性があります。

そのため、非同期処理においてキャンセルはリソース管理やパフォーマンスの観点から重要です。

この記事ではCancellationTokenを使用して非同期のタスクをキャンセルする方法をサンプルコードを交えながら紹介します。ぜひ参考にしてみてください。

オススメの参考書

C#の使い方が丁寧に解説しており、「基礎からしっかりと学びたい」という初心者の方にオススメの一冊です。サンプルコードも記載してあり、各章の最後に復習問題があるので理解度を確認しながら読み進めることができます。新しい C# のバージョンにも対応している書籍です。

記事の内容

CancellationTokenとは?

CancellationTokenは、非同期処理を実装する時に非同期処理をキャンセルするための仕組みです。

CancellationTokenSource クラスのオブジェクトを生成し、CancellationTokenをタスクに渡すことで、タスクの実行中にキャンセルをトリガーにすることができます。

非同期メソッド側でCancellationTokenを受け取れるように、メソッドの引数の最後でCancellationTokenを受け取るようにします。

// メソッドの引数の最後でCancellationTokenを受け取る
async Task TaskAsync(string str, CancellationToken cancellationToken)
{
   // 何かしらの処理
}

非同期メソッドの内部で更に別の非同期メソッドを呼び出す場合は、受け取ったCancellationTokenを別の非同期メソッドに渡します。このようにしてCancellationTokenを伝搬させていくことにより、非同期処理のキャンセルを実現します。

非同期処理をキャンセルする方法

非同期のキャンセル処理ですが、以下の内容を実装する必要があります。

  • CancellationTokenSourceのオブジェクトを作成する
  • 非同期メソッドの引数にCancellationTokenをつける
  • 非同期メソッドにCancellationTokenを渡す
  • キャンセルしたいタイミングでCancelメソッドを呼び出す
  • trycatchで例外(OperationCancelledException)をキャッチする

上から順番に説明していきます。

CancellationTokenSourceのオブジェクトを作成する

キャンセルの制御を行うために、CancellationTokenSourceクラスのオブジェクトを作成します。

var cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;

非同期メソッドの引数にCancellationTokenをつける

非同期のメソッドの引数の最後にCancellationTokenを実装します。CancellationTokenSourceオブジェクトのCancellationTokenを引数として、このメソッドに渡します。

async Task TaskAsync(int count, CancellationToken cancellationToken)
{
   // 何かしらの処理
}

非同期メソッドのサフィックスはAsyncにしておきましょう。非同期メソッドであることが一目瞭然です。

非同期メソッドの内部は引数のcountの数だけループして数値を加算する処理にします。

ここで重要なのが、1つのループ毎にThrowIfCancellationRequestedメソッドを呼びして、キャンセル要求があった場合に例外をスローして処理を中断できるようにしておくことです。

ThrowIfCancellationRequestedメソッドはCancellationTokenがキャンセル状態になっていた時にOperationCanceledExceptionという例外が発行されます。

またはif条件でIsCancellationRequestedプロパティの状態をチェックして、trueなら処理を中断できるようにします。

IsCancellationRequestedプロパティはCancellationTokenがキャンセル状態になっていた時にtrueになります。

async Task TaskAsync(int count, CancellationToken cancellationToken)
{
    var sum = 0;

    await Task.Run(() =>
    {
        for (int i = 0; i < count; i++)
        {
            // キャンセルが要求されたら例外をスローするメソッド
            cancellationToken.ThrowIfCancellationRequested();
            // if (cancellationToken.IsCancellationRequested) { return; }

            sum += 1;
            Debug.WriteLine($"加算結果:{sum}");
        }
    }, cancellationToken);
}

Cancelメソッドを呼び出す

キャンセルするには、CancellationTokenSourceCancelメソッドを呼び出します。

var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.Cancel();

例外をキャッチする

非同期処理をキャンセルするとOperationCanceledExceptionという例外が発行されるので、trycatchで例外をキャッチします。

この例外が非同期処理メソッド内から外に向かって発行された場合、そのメソッドに紐づいたタスクはキャンセル扱いになります。

var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.Cancel();

非同期処理をキャンセルすると、例外が発行されることを覚えておきましょう。

サンプルコード

上記の内容を踏まえて、以下のサンプルコードを確認してみましょう。

スタートボタンをクリックすると、非同期で10000回ループして1を加算するコードです。加算している最中にキャンセルボタンをクリックすると、加算を中断します。

private CancellationTokenSource cancellationTokenSource;

// スタートボタンのクリックイベントハンドラ
async void Button_Click(object sender, RoutedEventArgs e)
{
    cancellationTokenSource = new CancellationTokenSource();
    CancellationToken cancellationToken = cancellationTokenSource.Token;

    try
    {
        await TaskAsync(10000, cancellationToken);
    }
    catch (OperationCanceledException ex)
    {
        Debug.WriteLine(ex.Message);
    }
    finally
    {

    }
}

// キャンセルボタンのクリックイベントハンドラ
private void Cancel_Click(object sender, RoutedEventArgs e)
{
    cancellationTokenSource?.Cancel();
}

// 非同期タスク
async Task TaskAsync(int count, CancellationToken cancellationToken)
{
    var sum = 0;

    await Task.Run(() =>
    {
        for (int i = 0; i < count; i++)
        {
            // キャンセルが要求されたら例外をスローするメソッド
            cancellationToken.ThrowIfCancellationRequested();

            sum += 1;
            Debug.WriteLine($"加算結果:{sum}");
        }
    }, cancellationToken);
}

このコードを実行して加算途中でキャンセルボタンをクリックした結果が以下になります。

加算結果:1
加算結果:2
加算結果:3
加算結果:4
例外がスローされました: 'System.OperationCanceledException' (mscorlib.dll の中)
操作は取り消されました。

Cancelメソッドを呼びだしたタイミングでOperationCanceledExceptionが発行され、出力結果のように非同期処理がキャンセルされます。

まとめ

この記事では C# の非同期タスクのキャンセル方法について紹介をしました。

C# のCancellationTokenを使うことで、非同期プログラミングにおけるキャンセル機能の実装が可能になります。適切なキャンセル処理を行うことで、リソースリークやアプリケーションのパフォーマンス低下を防ぎます。

ぜひ扱えるようになっておきましょう。

以上、最後まで読んでいただきありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
記事の内容