Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/arduino/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在任务执行委托内部使用取消支持延迟的正确方式是什么?_C#_.net_Task Parallel Library_Cancellationtokensource_Cancellation Token - Fatal编程技术网

C# 在任务执行委托内部使用取消支持延迟的正确方式是什么?

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毫秒后取消包含

我在MSDN上或这里都没有看到任何关于如何实现这一点的具体提及。用例有点模糊,但我怀疑仍然有效

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语句可以帮助确认委托实际上已被取消(由于未执行这些语句),这使我无法在
任务中使用第二个块中的代码。运行
。我创建了一个展示整个问题及其解决方案的网站。