CancellationTokenプロパティの使用方法は?


117

前のクラス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();
      }
    }
  }
}

2
docs.microsoft.com/en-us/dotnet/standard/threading/…にCancellationTokenSource、非同期メソッドでの使用、ポーリングでの長時間実行メソッド、およびコールバックの使用のかなり良い例があります。
Ehtesh Choudhury

この記事では、特定のケースに従ってトークンを処理するために必要なオプションを示します。
Ognyan Dimitrov

回答:


140

次のように作業メソッドを実装できます。

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

それでおしまい。キャンセルは常に自分で処理する必要があります-終了する適切なタイミングでメソッドを終了します(これにより、作業とデータが一貫した状態になります)

更新:私は書いていないことを好むwhile (!cancelToken.IsCancellationRequested)ことが多いあなたはループ本体の間で安全に実行を停止することができますいくつかの出口ポイントがあるので、ループは通常、(反復などのコレクション内のすべての項目を超える)の出口にはいくつかの論理条件を持っています。そのため、意図が異なるため、これらの条件を混在させない方がよいと考えています。

回避に関する注意事項CancellationToken.ThrowIfCancellationRequested()

Eamon Nerbonneによる問題のコメント

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


1
ありがとう!これは、オンラインのテキストには従いませんが、非常に権威があります( "C#4.0 in a Nutshell"?)。「いつも」について教えてください。
2013

1
これは、実践と経験から来ています=)。私はどこからこれを知っているのか思い出せません。実際にはThread.Abort()を使用して、外部からワーカースレッドを例外的に中断できるため、「常に必要」を使用しましたが、これは非常に悪い習慣です。ちなみに、CancellationToken.ThrowIfCancellationRequested()を使用することも、「自分でキャンセルを処理する」ことと同じです。
サーシャ

1
@OleksandrPshenychnyy while(true)をwhile(!cancelToken.IsCancellationRequested)で置き換えることを意味しました。これは役に立ちました!ありがとう!
Doug Dawson

1
@Fulproofランタイムは、プロセスがどこで中断されるかを知るのに十分スマートではないため、ランタイムが実行中のコードをキャンセルする一般的な方法はありません。場合によっては、単にループを終了することも可能です。それ以外の場合は、より複雑なロジックが必要です。つまり、トランザクションをロールバックし、リソースを解放する必要があります(ファイルハンドルやネットワーク接続など)。これが、コードを書かずにタスクをキャンセルする魔法の方法がない理由です。あなたが考えるのはプロセスを殺すようなものですが、それはキャンセルではありません。それはクリーンアップできないためにアプリケーションに起こり得る最悪の事態の1つです。
user3285954 2014年

1
@kosist手動で開始する操作をキャンセルする予定がない場合は、CancellationToken.Noneを使用できます。もちろん、システムプロセスが強制終了されると、すべてが中断され、CancellationTokenはそれとは関係ありません。つまり、操作をキャンセルするために本当に使用する必要がある場合にのみ、CancellationTokenSourceを作成する必要があります。使わないものを作る意味はありません。
サーシャ

26

あずきっく

あなたはstackoverflowに投稿されたすべてを盲目的に信頼すべきではありません。Jensコードのコメントは正しくありません。このパラメーターは、例外がスローされるかどうかを制御しません。

MSDNは、そのパラメーターが何を制御しているのかを非常に明確にしています。読みましたか? http://msdn.microsoft.com/en-us/library/dd321703(v=vs.110).aspx

場合はthrowOnFirstExceptiontrueで、例外はすぐに処理されてから、残りのコールバックや解約の操作を防止し、キャンセルするコールのうちに伝播します。throwOnFirstExceptionがfalseの場合 、このオーバーロードはにスローされたすべての例外を集約します。AggregateExceptionそのため、例外をスローする1つのコールバックが、他の登録済みコールバックの実行を妨げることはありません。

CancellationTokenSourceトークン自体ではなくキャンセルが呼び出され、ソースが管理する各トークンの状態を変更するため、変数名も間違っています。


また、トークンのキャンセルの提案使い方についてはこちらのドキュメント(TAP)に見て: docs.microsoft.com/en-us/dotnet/standard/...
Epstone

1
これは非常に有用な情報ですが、質問の答えにはなりません。
11nallan11 2018

16

キャンセルトークンを使用してタスクを作成できます。バックグラウンドでアプリを起動すると、このトークンをキャンセルできます。

PCL https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/app-lifecycleでこれを行うことができます

var cancelToken = new CancellationTokenSource();
Task.Factory.StartNew(async () => {
    await Task.Delay(10000);
    // call web API
}, cancelToken.Token);

//this stops the Task:
cancelToken.Cancel(false);

AntherソリューションはXamarin.Formsのユーザータイマーであり、アプリがバックグラウンドに移動したらタイマーを停止し ますhttps://xamarinhelp.com/xamarin-forms-timer/


10

例外処理せずにお使いいただけますThrowIfCancellationRequested

の使用はThrowIfCancellationRequestedTask(ではなくThread)内から使用するためのものです。内で使用する場合Task、自分で例外を処理する必要はありません(そしてUnhandled Exceptionエラーが発生します)。その結果Task、が終了し、Task.IsCancelledプロパティはTrueになります。例外処理は必要ありません。

あなたの特定のケースでは、変更ThreadTask

Task t = null;
try
{
    t = Task.Run(() => Work(cancelSource.Token), cancelSource.Token);
}

if (t.IsCancelled)
{
    Console.WriteLine("Canceled!");
}

なぜあなたは使っt.Start()ているのTask.Run()ですか?
Xander Luciano

1
@XanderLuciano:この例では特定の理由はなく、Task.Run()がより良い選択でした。
タイタス

5

CancellationTokenタスクにを渡す必要があります。タスクは定期的にトークンを監視して、キャンセルが要求されているかどうかを確認します。

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;  
Task task = Task.Run(() => {     
  while(!token.IsCancellationRequested) {
      Console.Write("*");         
      Thread.Sleep(1000);
  }
}, token);
Console.WriteLine("Press enter to stop the task"); 
Console.ReadLine(); 
cancellationTokenSource.Cancel(); 

この場合、キャンセルがリクエストTaskされ、RanToCompletion状態を持つと操作は終了します。タスクがキャンセルされたことを確認したい場合は、を使用ThrowIfCancellationRequestedしてOperationCanceledException例外をスローする必要があります。

Task task = Task.Run(() =>             
{                 
    while (!token.IsCancellationRequested) {
         Console.Write("*");                      
        Thread.Sleep(1000);                 
    }           
    token.ThrowIfCancellationRequested();               
}, token)
.ContinueWith(t =>
 {
      t.Exception?.Handle(e => true);
      Console.WriteLine("You have canceled the task");
 },TaskContinuationOptions.OnlyOnCanceled);  

Console.WriteLine("Press enter to stop the task");                 
Console.ReadLine();                 
cancellationTokenSource.Cancel();                 
task.Wait(); 

これがよりよく理解するのに役立つことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.