1. ホーム
  2. c#

タスクのキャンセルで例外が発生する

2023-12-24 14:47:01

質問

タスクについて読んだところでは、次のコードは例外を投げずに現在実行中のタスクをキャンセルする必要があります。私は、タスク キャンセルの全体のポイントは、スレッドを中止することなくタスクに停止するように丁寧に "ask" することであるという印象を受けました。

次のプログラムからの出力は。

例外をダンプする

[OperationCanceledException](オペレーションキャンセルドエクセプション

最後に計算された素数をキャンセルして返します。

キャンセル時に例外が発生しないようにしたいと考えています。どのようにしたらこれを達成できますか?

void Main()
{
    var cancellationToken = new CancellationTokenSource();

    var task = new Task<int>(() => {
        return CalculatePrime(cancellationToken.Token, 10000);
    }, cancellationToken.Token);

    try
    {
        task.Start();
        Thread.Sleep(100);
        cancellationToken.Cancel();
        task.Wait(cancellationToken.Token);         
    }
    catch (Exception e)
    {
        Console.WriteLine("Dumping exception");
        e.Dump();
    }
}

int CalculatePrime(CancellationToken cancelToken, object digits)
{  
    int factor; 
    int lastPrime = 0;

    int c = (int)digits;

    for (int num = 2; num < c; num++)
    { 
        bool isprime = true;
        factor = 0; 

        if (cancelToken.IsCancellationRequested)
        {
            Console.WriteLine ("Cancelling and returning last calculated prime.");
            //cancelToken.ThrowIfCancellationRequested();
            return lastPrime;
        }

        // see if num is evenly divisible 
        for (int i = 2; i <= num/2; i++)
        { 
            if ((num % i) == 0)
            {             
                // num is evenly divisible -- not prime 
                isprime = false; 
                factor = i; 
            }
        } 

        if (isprime)
        {
            lastPrime = num;
        }
    }

    return lastPrime;
}

どのように解決するのですか?

この行で明示的にExceptionを投げています。

cancelToken.ThrowIfCancellationRequested();

タスクを優雅に終了させたいのであれば、単にこの行を削除する必要があります。

一般的には、潜在的に余分なコードを実行することなく、現在の処理を確実に中止するための制御メカニズムとして、これを使用します。また、タスクの終了を確認するために ThrowIfCancellationRequested() と機能的に同等であるためです。

if (token.IsCancellationRequested) 
    throw new OperationCanceledException(token);

を使う場合 ThrowIfCancellationRequested() を使うと、タスクは次のようになります。

int CalculatePrime(CancellationToken cancelToken, object digits) {
    try{
        while(true){
            cancelToken.ThrowIfCancellationRequested();

            //Long operation here...
        }
    }
    finally{
        //Do some cleanup
    }
}

また Task.Wait(CancellationToken) はトークンがキャンセルされた場合、例外を投げます。このメソッドを使用するには、Wait 呼び出しをラップして Try...Catch ブロックで囲む必要があります。

MSDN タスクをキャンセルする方法