タスクのキャンセル

System.Threading.Tasks.Task および System.Threading.Tasks.Task<TResult> の各クラスでは、キャンセル トークンを使用した取り消し処理をサポートしています。 詳細については、「マネージド スレッドのキャンセル」を参照してください。 Task クラスのキャンセル処理には、キャンセル可能な操作を表すユーザー デリゲートと、キャンセルを要求したコードとの連携が含まれます。 キャンセル処理が正常に実行されるには、要求コードが CancellationTokenSource.Cancel メソッドを呼び出し、ユーザー デリゲートが操作を適時に終了する必要があります。 次のオプションのいずれかを使用して操作を終了できます。

  • デリゲートから戻ります。 多くの場合、この処理で十分ですが、この方法で取り消されたタスク インスタンスは、 TaskStatus.Canceled 状態ではなく、 TaskStatus.RanToCompletion 状態に遷移します。

  • OperationCanceledException をスローし、これをキャンセルが要求されたトークンに渡します。 これを行うには、 ThrowIfCancellationRequested メソッドを使用する方法をお勧めします。 この方法で取り消されたタスクは Canceled 状態に遷移し、タスクがキャンセル要求に応答したことを確認するために呼び出し元のコードによって使用されます。

次の例は、例外をスローするタスクのキャンセルの基本的なパターンを示しています。 ユーザー デリゲートとタスク インスタンス自体にトークンが渡されることに注意してください。

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var tokenSource2 = new CancellationTokenSource();
        CancellationToken ct = tokenSource2.Token;

        var task = Task.Run(() =>
        {
            // Were we already canceled?
            ct.ThrowIfCancellationRequested();

            bool moreToDo = true;
            while (moreToDo)
            {
                // Poll on this property if you have to do
                // other cleanup before throwing.
                if (ct.IsCancellationRequested)
                {
                    // Clean up here, then...
                    ct.ThrowIfCancellationRequested();
                }
            }
        }, tokenSource2.Token); // Pass same token to Task.Run.

        tokenSource2.Cancel();

        // Just continue on this thread, or await with try-catch:
        try
        {
            await task;
        }
        catch (OperationCanceledException e)
        {
            Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
        }
        finally
        {
            tokenSource2.Dispose();
        }

        Console.ReadKey();
    }
}
Imports System.Threading
Imports System.Threading.Tasks

Module Test
    Sub Main()
        Dim tokenSource2 As New CancellationTokenSource()
        Dim ct As CancellationToken = tokenSource2.Token

        Dim t2 = Task.Factory.StartNew(Sub()
                                           ' Were we already canceled?
                                           ct.ThrowIfCancellationRequested()

                                           Dim moreToDo As Boolean = True
                                           While moreToDo = True
                                               ' Poll on this property if you have to do
                                               ' other cleanup before throwing.
                                               If ct.IsCancellationRequested Then

                                                   ' Clean up here, then...
                                                   ct.ThrowIfCancellationRequested()
                                               End If

                                           End While
                                       End Sub _
        , tokenSource2.Token) ' Pass same token to StartNew.

        ' Cancel the task.
        tokenSource2.Cancel()

        ' Just continue on this thread, or Wait/WaitAll with try-catch:
        Try
            t2.Wait()

        Catch e As AggregateException

            For Each item In e.InnerExceptions
                Console.WriteLine(e.Message & " " & item.Message)
            Next
        Finally
            tokenSource2.Dispose()
        End Try

        Console.ReadKey()
    End Sub
End Module

より完全なコード例については、「方法:タスクとその子を取り消す」を参照してください。

タスク インスタンスがユーザー コードによってスローされた OperationCanceledException を確認した場合は、例外のトークンと関連付けられたトークン (タスクを作成した API に渡されたトークン) とを比較します。 これらのトークンが同一であり、トークンの IsCancellationRequested プロパティから true が返されると、タスクはこれをキャンセルの受信確認と解釈し、Canceled 状態に遷移します。 Wait メソッドまたは WaitAll メソッドを使用してタスクを待機しない場合、タスクの状態は Canceledに設定されます。

タスクが Canceled 状態に遷移するのを待っていると、 System.Threading.Tasks.TaskCanceledException 例外 ( AggregateException 例外にラップされている) がスローされます。 この例外は、障害のある状況ではなく、正常なキャンセル処理を示すことに注意してください。 このため、タスクの Exception プロパティは nullを返します。

トークンの IsCancellationRequested プロパティが false を返した場合、または例外のトークンがタスクのトークンと一致しない場合、 OperationCanceledException は標準の例外のように扱われるため、タスクは Faulted 状態に遷移します。 他の例外が存在する場合も、タスクが Faulted 状態に遷移することに注意してください。 完了したタスクの状態は Status プロパティで取得できます。

キャンセルが要求された後も、タスクが一部の項目の処理を継続する可能性があります。

関連項目