如何使用“取消令牌”属性?

与前面的代码 对于类 RulyCanceler相比,我希望使用 CancellationTokenSource运行代码。

如何像 取消令牌中提到的那样使用它,即不抛出/捕获异常?我可以使用 IsCancellationRequested属性吗?

我试图这样使用它:

cancelToken.ThrowIfCancellationRequested();

还有

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

但是这给了方法 Work(CancellationToken cancelToken)中的 cancelToken.ThrowIfCancellationRequested();一个运行时错误:

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();
}
}
}
}
277155 次浏览

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

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

就是这样。您总是需要自己处理取消操作-在适当的时候退出方法(以便您的工作和数据处于一致的状态)

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

注意避免 CancellationToken.ThrowIfCancellationRequested():

Eamon Nerbonne有问题的评论 :

用一系列优雅的 IsCancellationRequested退出检查代替 ThrowIfCancellationRequested正如这个答案所说。但这不仅仅是一个实现细节; 这会影响可观察到的行为: 任务将不再以被取消的状态结束,而是以 RanToCompletion结束。这不仅会影响显式的状态检查,更微妙的是,还会影响使用 ContinueWith的任务链,具体取决于使用的 TaskContinuationOptions。我认为避免 ThrowIfCancellationRequested是一个危险的建议。

可以使用 ThrowIfCancellationRequested而不处理异常!

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

在您的特定情况下,将 Thread更改为 Task

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


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

您可以创建一个任务与取消令牌,当您应用程序去后台您可以取消这个令牌。

您可以在 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 的用户定时器 Https://xamarinhelp.com/xamarin-forms-timer/

您必须将 CancellationToken传递给 Task,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();

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

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