微软70-483考试引用书CancellationToken的使用,第一种用signal取消的方式是抛异常,然后介绍第二种:
Instead of catching the exception, you can also add a continuation Task that executes only when the Task is canceled. In this Task, you have access to the exception that was thrown, and you can choose to handle it if that’s appropriate. Listing 1-44 shows what such a continuation task would look like
这是 list 1-44:
Task task = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write("*");
Thread.Sleep(1000);
}
}, token).ContinueWith((t) =>
{
t.Exception.Handle((e) => true);
Console.WriteLine("You have canceled the task");
}, TaskContinuationOptions.OnlyOnCanceled);
这是我完整的主要方法代码:
static void Main(string[] args)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
Task task = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write("*");
Thread.Sleep(1000);
}
}, token).ContinueWith((t) =>
{
t.Exception.Handle((e) => true);
Console.WriteLine("You have canceled the task");
}, TaskContinuationOptions.OnlyOnCanceled);
Console.ReadLine();
cancellationTokenSource.Cancel();
task.Wait();
Console.ReadLine();
}
但是,与它所说的不同,当我按 Enter 时,异常 (AggregationException) 仍然抛给 task.Wait()
调用中的 Main 方法。此外,如果我删除该调用,则第二个 Task 永远不会运行(不会抛出异常)。我做错了什么吗?是否可以在不使用 try-catch
的情况下处理异常?
最佳答案
为了明确说明问题,您的第二个延续没有执行,但您认为它应该:
static void Main(string[] args)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
Task task = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write("*");
Thread.Sleep(1000);
}
}, token).ContinueWith((t) =>
{ // THIS
t.Exception.Handle((e) => true); // ISN'T
Console.WriteLine("You have canceled the task"); // EXECUTING
}, TaskContinuationOptions.OnlyOnCanceled);
Console.ReadLine();
cancellationTokenSource.Cancel();
task.Wait();
Console.ReadLine();
}
第二个延续未执行,因为您必须使用 token.ThrowIfCancellationRequested()
才能触发它:
Task task = Task.Run(() =>
{
while (true)
{
token.ThrowIfCancellationRequested(); // <-- NOTICE
Console.Write("*");
Thread.Sleep(1000);
}
}, token).ContinueWith((t) =>
{
Console.WriteLine("From Continuation: " + t.Status);
Console.WriteLine("You have canceled the task");
}, TaskContinuationOptions.OnlyOnCanceled);
// OUTPUT:
// ***
// From Continuation: Canceled
// You have canceled the task
因为 task.Status
Canceled
调用了第二个延续。下一个片段不会触发第二次继续,因为 task.Status
未设置为 Canceled
:
Task task = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write("*");
Thread.Sleep(1000);
}
}, token).ContinueWith((t) =>
{
Console.WriteLine("From Continuation: " + t.Status);
Console.WriteLine("You have canceled the task");
}, TaskContinuationOptions.OnlyOnCanceled);
// OUTPUT:
// AggregationException
如前所述,未调用第二个延续。让我们通过删除 OnlyOnCanceled
子句来强制执行它:
Task task = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write("*");
Thread.Sleep(1000);
}
}, token).ContinueWith((t) =>
{
Console.WriteLine("From Continuation: " + t.Status);
Console.WriteLine("You have NOT canceled the task");
}); // <-- OnlyOnCanceled is gone!
// OUTPUT:
// ***
// From Continuation: RanToCompletion
// You have NOT canceled the task
// (no AggregationException thrown)
请注意,即使调用了 .Cancel()
,延续内的 task.Status
也是 RanToCompletion
。另请注意,没有抛出 AggregationException
。这表明只是从 token 源调用.Cancel()
不会将任务状态设置为已取消
。
当只调用.Cancel()
而不调用.ThrowIfCancellationRequested()
时,AggregationException
实际上是任务成功的标志消除。引用MSDN article :
If you are waiting on a
Task
that transitions to theCanceled
state, aSystem.Threading.Tasks.TaskCanceledException
exception (wrapped in anAggregateException
exception) is thrown. Note that this exception indicates successful cancellation instead of a faulty situation. Therefore, the task'sException
property returnsnull
.
这让我得出了一个宏大的结论:
list 1-44 是一个 known error .
我的所有代码中都省略了你的 t.Exception...
行,因为“任务的 Exception
属性返回 null
”成功取消后。 should 行已从 list 1-44 中省略。看起来他们将以下两种技术混为一谈:
- 我回答的第一段是取消任务的有效方法。
OnlyOnCanceled
延续被调用并且没有抛出异常。 - 我的回答的第二个片段也是取消任务的有效方法,但不会调用
OnlyOnCanceled
延续,并抛出 AggregationException 供您在Task.Wait( )
免责声明:这两个片段都是取消任务的有效方法,但它们可能存在我不知道的行为差异。
关于c# - Task.ContinueWith 不适用于 OnlyOnCanceled,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33156963/