C# 在任务执行委托内部使用取消支持延迟的正确方式是什么?
我在MSDN上或这里都没有看到任何关于如何实现这一点的具体提及。用例有点模糊,但我怀疑仍然有效C# 在任务执行委托内部使用取消支持延迟的正确方式是什么?,c#,.net,task-parallel-library,cancellationtokensource,cancellation-token,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毫秒后取消包含
var cancel = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => { Task.Delay(1000, cancel.Token).Wait(); }, cancel.Token);
cancel.CancelAfter(100);
task.Wait();
上述代码将尝试在100毫秒后取消包含分离的子延迟任务的任务
,并等待任务
完成,该任务将生成一个聚合异常
(由于取消)。问题是任务
会出现故障,而不是被取消。这是预期的行为,因为延迟任务未附加到父任务
,即使两者共享相同的取消令牌
我的问题特别涉及如何将任务.Delay
附加到已在运行的任务。如果您有权访问父任务,是否可以执行此操作?如果不可能,或者不访问父任务实例就不可能,那么处理此场景的正确方法是什么
我能想到的最好的解决办法是将延迟任务的Wait
包装在一个try/finally块中,并显式地尝试冒泡任务取消
try { Task.Delay(1000, cancel.Token).Wait(); } finally { cancel.Token.ThrowIfCancellationRequested(); }
虽然很有效,但感觉不太对,但我不确定是否有更好的方法来实现这一点。理想的结果是,如果发生取消,父任务将转到cancelled
,而不是Faulted
。因此,如果取消发生在分离的子任务中,则父任务仍应转换为cancelled
注意:我故意省略了async/wait,只是因为它似乎没有改变问题或结果。如果情况并非如此,请提供一个示例。因此,当一个
OperationCanceledException
被抛出并在其内部取消捕获,并且其关联的CancellationToken
被取消时,任务被视为已取消
在您的情况下,引发的异常是aggregateeexception
,它包含TaskCanceledException
(这是一个OperationCanceledException
)而不是直接包含TaskCanceledException
有一个简单的方法来解决这个问题。您可以使用Task.GatAwaiter().GetResult()
,而不是使用Task.Wait
将任何异常包装在aggregateeexception
包装器中,而不是使用Task.GatAwaiter().GetResult()同步阻塞。这就是await
在async 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 Wait
,则不会出现与Task.Wait不同的问题。Wait
会引发TaskCanceledException
本身:
var cancel = new CancellationTokenSource();
var task = Task.Run(() => Task.Delay(1000, cancel.Token), cancel.Token);
cancel.CancelAfter(100);
task.Wait();
我想这只是一个例子。真正的生产代码不应该像这样,因为您在异步操作上同步阻塞,而异步操作反过来又在异步操作上同步阻塞。GetAwaiter().GetResult()
正是我要找的。该代码实际上是要编写一个测试,以确认可以取消已启动的任务。延迟只是为了模拟取消正在执行它的(长时间运行)委托的任务。在我的测试中,我有一些post delay语句可以帮助确认委托实际上已被取消(由于未执行这些语句),这使我无法在任务中使用第二个块中的代码。运行。我创建了一个展示整个问题及其解决方案的网站。