1. ホーム
  2. c#

[解決済み] CancellationTokenプロパティを使用するには?

2022-05-16 22:23:26

質問

前のコードと比較すると RulyCanceler クラスの場合 を使ってコードを実行したかったのですが CancellationTokenSource .

にあるように、どのように使用すればよいのでしょうか? キャンセルトークン で述べたように、つまり例外をスロー/キャッチせずに使用するにはどうすればよいですか? また IsCancellationRequested プロパティを使用できますか?

このように使おうとしました。

cancelToken.ThrowIfCancellationRequested();

try
{
  new Thread(() => Work(cancelSource.Token)).Start();
}
catch (OperationCanceledException)
{
  Console.WriteLine("Canceled!");
}

でランタイムエラーが発生します。 cancelToken.ThrowIfCancellationRequested(); というメソッドで Work(CancellationToken cancelToken) :

System.OperationCanceledException was unhandled
  Message=The operation was canceled.
  Source=mscorlib
  StackTrace:
       at System.Threading.CancellationToken.ThrowIfCancellationRequested()
       at _7CancellationTokens.Token.Work(CancellationToken cancelToken) in C:\xxx\Token.cs:line 33
       at _7CancellationTokens.Token.<>c__DisplayClass1.<Main>b__0() in C:\xxx\Token.cs:line 22
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

正常に実行できたコードは、新しいスレッドでOperationCanceledExceptionを捕捉しています。

using System;
using System.Threading;
namespace _7CancellationTokens
{
  internal class Token
  {
    private static void Main()
    {
      var cancelSource = new CancellationTokenSource();
      new Thread(() =>
      {
         try
         {
           Work(cancelSource.Token); //).Start();
         }
         catch (OperationCanceledException)
         {
            Console.WriteLine("Canceled!");
         }
         }).Start();

      Thread.Sleep(1000);
      cancelSource.Cancel(); // Safely cancel worker.
      Console.ReadLine();
    }
    private static void Work(CancellationToken cancelToken)
    {
      while (true)
      {
        Console.Write("345");
        cancelToken.ThrowIfCancellationRequested();
      }
    }
  }
}

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

以下のように作業方法を実装することができます。

private static void Work(CancellationToken cancelToken)
{
    while (true)
    {
        if(cancelToken.IsCancellationRequested)
        {
            return;
        }
        Console.Write("345");
    }
}

以上です。キャンセル処理は必ず自分で行い、適切なタイミングでメソッドを終了させる必要があります(自分の作業やデータが一貫した状態になるように)。

UPDATEです。 私は while (!cancelToken.IsCancellationRequested) というのも、ループ本体で安全に実行を停止できる出口が少ないことが多く、ループには通常、終了するための論理的な条件(コレクション内の全項目を反復処理する、など)があるからです。そのため、これらの条件は異なる意図を持っているので、混在させない方が良いと思います。

を避けるための注意点 CancellationToken.ThrowIfCancellationRequested() :

問題のコメント によって Eamon Nerbonne :

<ブロッククオート

...を置き換える ThrowIfCancellationRequested に対する多くのチェックで IsCancellationRequested は、この回答が言うように、優雅に終了します。しかし、これは単なる実装の詳細ではなく、観察可能な動作に影響します。タスクはもはやキャンセルされた状態で終了するのではなく RanToCompletion . そして、これは明示的な状態チェックだけでなく、より微妙なことですが、例えば、タスクチェーンで ContinueWith でのタスクチェインにも影響します。 TaskContinuationOptions が使われます。私は、避けるべきは ThrowIfCancellationRequested は危険なアドバイスです。