在微软70-483关于使用CancellationToken的考试的参考书中,第一种取消信号的方法是抛出异常,然后引入第二种:
您也可以添加延续,而不是捕获异常 仅在取消任务时执行的任务。在这个任务中,你 可以访问抛出的异常,您可以选择 如果合适的话,处理它。清单1-44显示了这样的内容 延续任务看起来像
以下是清单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时,在task.Wait()
调用时仍然会向Main方法抛出异常(AggregationException)。此外,如果我删除该调用,第二个任务永远不会运行(不会抛出异常)。有什么我做错了吗?是否可以在不使用try-catch
的情况下处理异常?
答案 0 :(得分:7)
要明确说明问题,您的第二个延续不会执行,但您认为它应该:
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
。这表明仅从令牌源调用.Cancel()
未将任务状态设置为Canceled
。
仅调用.Cancel()
且未调用.ThrowIfCancellationRequested()
时,AggregationException
实际上是成功取消任务的指标。引用MSDN article:
如果您正在等待转换到
Task
状态的Canceled
,则会抛出System.Threading.Tasks.TaskCanceledException
异常(包含在AggregateException
异常中)。请注意,此异常表示成功取消而不是出现故障。因此,任务的Exception
属性返回null
。
这引出了我的结论:
我的所有代码都省略了您的t.Exception...
行,因为“任务的Exception
属性会在成功取消后返回null
”。清单1-44中省略了 行。看起来他们正在混淆以下两种技术:
OnlyOnCanceled
延续并且不会抛出任何异常。OnlyOnCanceled
延续,并且在Task.Wait()
处理时会抛出AggregationException 免责声明:两个片段都是取消任务的有效方法,但它们可能在我不了解的行为方面存在差异。
答案 1 :(得分:2)
使用cancellationTokenSource.Cancel()
取消的任务实例将具有TaskStatus.RanToCompletion
状态,而不是TaskStatus.Canceled
状态。所以我认为您必须将TaskContinuationOptions.OnlyOnCanceled
更改为TaskContinuationOptions.OnlyOnRanToCompletion
您可以在MSDN上查看Task Cancellation了解详情。
以下是示例代码:
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.OnlyOnRanToCompletion);
Console.ReadLine();
cancellationTokenSource.Cancel();
try
{
task.Wait();
}
catch (AggregateException e)
{
foreach (var v in e.InnerExceptions)
Console.WriteLine(e.Message + " " + v.Message);
}
Console.ReadLine();
答案 2 :(得分:0)
试试这个:
Task task = Task.Run(() =>
{
while (true) {
token.ThrowIfCancellationRequested();
Console.Write("*");
Thread.Sleep(1000);
}
}, token).ContinueWith((t) =>
{
//t.Exception.Handle((e) => true); //there is no exception
Console.WriteLine("You have canceled the task");
}, TaskContinuationOptions.OnlyOnCanceled);