3

我在 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变得有问题而不是被取消。这是预期的行为,因为延迟任务未附加到 parent task,即使两者共享相同的取消令牌。

我的问题特别与您将如何将 a 附加Task.Delay到已经运行的任务有关。如果您有权访问父任务,甚至可以这样做吗?如果不可能,或者不能访问父任务实例,那么处理这种情况的正确方法是什么?

我能想到的最好的解决方法是将延迟任务包装Wait在 try/finally 块中,并明确地尝试使任务取消冒泡。

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

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

注意:我故意在这里省略了 async/await,只是因为它似乎并没有改变问题或结果。如果不是这种情况,请提供一个例子。

4

1 回答 1

1

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

在您的情况下,抛出的异常是AggregateException包含a (即 a )而不是直接的。TaskCanceledExceptionOperationCanceledExceptionTaskCanceledException

有一个简单的方法可以解决这个问题。您可以Task.Wait使用. 这就是在. 它抛出原始异常,如果有多个异常,则抛出第一个:AggregateExceptiontask.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();

我认为这只是一个例子。真正的生产代码不应与此类似,因为您在异步操作上同步阻塞,而异步操作又在异步操作上同步阻塞。

于 2015-04-24T02:17:07.483 回答