141

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

如何使用Cancellation Tokens中提到的它,即不抛出/捕获异常?我可以使用该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();
      }
    }
  }
}
4

4 回答 4

172

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

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. 这不仅会影响显式状态检查,还会更微妙地影响与 eg 的任务链接ContinueWith,具体取决于所TaskContinuationOptions使用的。我会说避免ThrowIfCancellationRequested是危险的建议。

于 2013-02-25T13:20:50.550 回答
19

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

您可以在 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);

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

于 2017-07-24T01:37:09.230 回答
14

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

// 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(); 

在这种情况下,操作将在请求取消时结束,并且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(); 

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

于 2019-02-20T07:05:14.070 回答
12

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

的使用ThrowIfCancellationRequested意味着在 a Task(不是 a Thread)中使用。在 a 中使用时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!");
}
于 2016-06-24T08:57:52.130 回答