在任务执行委托中延迟取消支持的正确方法是什么?

时间:2015-04-24 02:02:18

标签: c# .net task-parallel-library cancellationtokensource cancellation-token

我在MSDN上或在此处没有看到任何具体提及如何实现此目的。用例有点模糊,但我怀疑仍然有效。

var cancel = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => { Task.Delay(1000, cancel.Token).Wait(); }, cancel.Token);
cancel.CancelAfter(100);
task.Wait();

以上代码将尝试在100毫秒后取消包含 分离的 子延迟任务的task,并等待task完成将产生AggregateException(由于取消)。这样做的问题是task出现故障而不是取消。这是预期的行为,因为延迟任务未附加到父task,即使两者都共享相同的取消令牌。

我的问题具体涉及如何将Task.Delay附加到已经运行的任务。如果您有权访问父任务,是否可以执行此操作?如果不可能,或者无法访问父任务实例,那么处理这种情况的正确方法是什么?

我能想出的最好的工作是将延迟任务Wait包装在try / finally块中,并明确尝试冒泡取消任务。

try { Task.Delay(1000, cancel.Token).Wait(); } finally { cancel.Token.ThrowIfCancellationRequested(); }

虽然有效,但感觉不是很正确,但我不确定是否有更好的方法来实现这一目标。如果取消发生,期望的结果是父任务转到Canceled而不是Faulted。因此,如果取消的起因发生在分离的子任务中,则父任务仍应转换为Canceled

注意 :我故意忘记了async / await,因为它似乎没有改变问题或结果。如果情况并非如此,请提供一个示例。

1 个答案:

答案 0 :(得分:1)

因此,当OperationCanceledException被抛出并且未被捕获且其关联的CancellationToken被取消时,任务被视为已取消。

在您的情况下,抛出的异常是AggregateException 包含 TaskCanceledExceptionOperationCanceledException)而不是TaskCanceledException直接。

有一种简单的方法可以解决这个问题。您可以使用Task.Wait,而不是使用AggregateException同步阻止包裹task.GatAwaiter().GetResult()包装中的任何异常。这是awaitasync-await中使用的内容。它会抛出原始异常,如果有多个则抛出第一个:

var cancel = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => { Task.Delay(1000, cancel.Token).GetAwaiter().GetResult(); }, cancel.Token);
cancel.CancelAfter(100);
task.Wait();

如果您使用async-await,则不会遇到此问题,因为它与Task.Wait不同,它会抛出TaskCanceledException本身:

var cancel = new CancellationTokenSource();
var task = Task.Run(() => Task.Delay(1000, cancel.Token), cancel.Token);
cancel.CancelAfter(100);
task.Wait();

我认为这只是一个例子。真正的生产代码不应该像这样,因为你在异步操作上同步阻塞,而异步操作又会在异步操作上同步阻塞。