如何使用CancellationToken属性?

Ful*_*oof 103 c# concurrency multithreading synchronization asynchronous

与上面的RulyCanceler类的代码相比,我想使用运行代码 CancellationTokenSource.

如何在取消令牌中提到它,即不抛出/捕获异常?我可以使用该IsCancellationRequested物业吗?

我试图像这样使用它:

cancelToken.ThrowIfCancellationRequested();
Run Code Online (Sandbox Code Playgroud)

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

但这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:
Run Code Online (Sandbox Code Playgroud)

我成功运行的代码在新线程中捕获了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();
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Sas*_*sha 129

您可以按如下方式实现工作方法:

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

而已.您总是需要自己处理取消 - 在适当的时间退出时退出方法(以便您的工作和数据处于一致状态)

更新:我不喜欢写作,while (!cancelToken.IsCancellationRequested)因为通常只有很少的出口点,你可以在循环体上安全地停止执行,循环通常有一些逻辑条件退出(迭代集合中的所有项目等).所以我认为最好不要将这些条件混合起来,因为它们有不同的意图.

  • @kosist 如果您不打算取消手动启动的操作,则可以使用 CancellationToken.None 。当然,当系统进程被杀死时,一切都会中断,CancellationToken 与此无关。所以是的,只有当您确实需要使用 CancellationTokenSource 来取消操作时,才应该创建它。创造你不使用的东西是没有意义的。 (3认同)
  • @OleksandrPshenychnyy 我的意思是用 while(!cancelToken.IsCancellationRequested) 替换 while(true) 。这很有帮助!谢谢! (2认同)

use*_*954 24

@ BrainSlugs83

你不应该盲目信任stackoverflow上发布的所有内容.Jens代码中的注释不正确,该参数不控制是否抛出异常.

MSDN非常清楚该参数控制的是什么,你读过它吗? http://msdn.microsoft.com/en-us/library/dd321703(v=vs.110).aspx

如果throwOnFirstException为true,则异常将立即传播出Cancel的调用,从而阻止处理剩余的回调和可取消的操作.如果throwOnFirstException为false,则此重载将聚合抛出到AggregateException中的任何异常,这样一个抛出异常的回调将不会阻止执行其他已注册的回调.

变量名也是错误的,因为在CancellationTokenSource上调用Cancel而不是令牌本身,并且源更改它管理的每个令牌的状态.


Mah*_*man 11

您必须将 传递CancellationToken给 Task,它会定期监视令牌以查看是否请求取消。

// CancellationTokenSource provides the token and have authority to cancel the token
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;  

// Task need to be cancelled with CancellationToken 
Task task = Task.Run(async () => {     
  while(!token.IsCancellationRequested) {
      Console.Write("*");         
      await Task.Delay(1000);
  }
}, token);

Console.WriteLine("Press enter to stop the task"); 
Console.ReadLine(); 
cancellationTokenSource.Cancel(); 
Run Code Online (Sandbox Code Playgroud)

在这种情况下,操作将在请求取消时结束,并且Task将有一个RanToCompletion状态。如果您想确认您的任务已被取消,您必须使用ThrowIfCancellationRequested抛出OperationCanceledException异常。

Task task = Task.Run(async () =>             
{                 
    while (!token.IsCancellationRequested) {
         Console.Write("*");                      
         await Task.Delay(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(); 
Run Code Online (Sandbox Code Playgroud)

希望这有助于更好地理解。

  • 为什么cancelationToken没有向下传递给`Task.Delay()`? (3认同)

Tit*_*tus 10

您可以在不处理异常的情况下使用ThrowIfCancellationRequested!

ThrowIfCancellationRequested的使用意味着在Task(而不是Thread)中使用.在Task中使用时,您不必自己处理异常(并获取Unhandled Exception错误).它将导致离开Task,并且Task.IsCancelled属性将为True.无需异常处理.

在您的特定情况下,将线程更改为任务.

try
{
  Task.Run(() => Work(cancelSource.Token), cancelSource.Token);
}
if (t.IsCancelled)
  Console.WriteLine("Canceled!");
}
Run Code Online (Sandbox Code Playgroud)

  • 您没有显示 ThrowIfCancellationRequested (2认同)

小智 10

您可以使用取消令牌创建任务,当您应用转到后台时,您可以取消此令牌.

您可以在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);
Run Code Online (Sandbox Code Playgroud)

Anther解决方案是Xamarin.Forms中的用户计时器,当应用程序转到后台时停止计时器 https://xamarinhelp.com/xamarin-forms-timer/

  • 两个问题。首先,使用 [Task.Run()](https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html“StartNew() 很危险 - Stephen Cleary”)。其次,这不会像您想象的那样取消底层等待的任务。在这种情况下,您需要将“cancelToken”传递给延迟:“Task.Delay(10000, cancelToken)”。通过令牌取消是合作性的。它需要传递给您希望能够在链中取消的每个等待。 (2认同)
  • 关于上面的第 2 点,[请参阅此处的“CancellationToken”部分](https://blog.stephencleary.com/2015/03/a-tour-of-task-part-9-delegate-tasks.html)。 (2认同)