取消时延续链中的哪个任务正在运行
本文关键字:任务 运行 时延 延续 取消 | 更新日期: 2023-09-27 18:34:43
我有一个Task
的延续链,可能会在发生超时后被取消。 我想确定取消发生时正在运行的任务。 这就是我要说的:
CancellationTokenSource cts = new CancellationTokenSource();
Task timeoutTask = Task.Delay(5000).ContinueWith((t) => cts.Cancel());
Task workChain = Task.Factory.StartNew((t) =>
{
Console.WriteLine("Running task " + Task.CurrentId);
Thread.Sleep(1000);
}, -1, cts.Token);
Task parent = workChain;
for (int i = 0; i < 10; i++)
{
parent = parent.ContinueWith((t, o) =>
{
Console.WriteLine("Running task " + Task.CurrentId);
Console.WriteLine("Last Task.AsyncState = " + t.AsyncState);
Thread.Sleep(1000);
}, i, cts.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current);
}
parent.ContinueWith((t) =>
{
Console.WriteLine("Cancel task " + Task.CurrentId);
Console.WriteLine("Last running Task.AsyncState = " + t.AsyncState);
}, TaskContinuationOptions.OnlyOnCanceled);
当我运行上述操作时,传递到OnlyOnCanceled
的先行不是取消时正在运行的任务。 我知道为什么:OnlyOnCanceled
任务由循环中创建的最后一个任务父级,当超时发生时,所有未完成的任务都会被标记为已取消而未启动。
让每个任务检查令牌的状态并将某些内容存储在其他地方在大多数情况下都是有效的,但是在一个任务完成后,下一个任务开始之前取消的可能性很小。 在这种情况下,我找不到有关第一个取消任务的任何信息。 我总是可以在任务开始时存储一些东西,如果它被取消,我总是可以存储其他东西,但这很快就会开始感觉很笨拙。
使可能正在运行的每个任务记录它已开始或停止执行。TPL 不会记录哪些任务在运行时运行。
我发现的东西对我有用,但仍然感觉不像它应该的那样干净:
- 主链中的每个任务都不采用
CancellationToken
,而是获得运行OnlyOnFaulted
的第二个延续(不是主链的一部分(。
这些任务中的每一个都在子任务 - 中执行其工作,然后通过调用
.Wait(CancellationToken)
来等待子任务。 取消令牌时,Wait 调用会引发异常,导致正在运行的任务出错。 这允许报告错误,而无需等待子任务完成。 - 主链中的每个任务都运行
OnlyOnRanToCompletion
。 - 主链末端运行
OnlyOnRanToCompletion
的一个任务报告整个链的成功(即没有超时(。
取消令牌后,当前正在运行的任务将停止等待其子任务,并显示 OperationCanceledException
。 该任务的侧分支通过特殊大小写前置AggregateException.InnerExceptions
中是否存在OperationCanceledException
来处理异常(或任何其他异常(。 由于主链中的任务出现故障,因此主链中的其他任务不会运行。
CancellationTokenSource cts = new CancellationTokenSource(25000);
Task workChain = Task.Factory.StartNew((o) =>
{
Console.WriteLine("Running task {0} with state {1}", Task.CurrentId, o);
Thread.Sleep(1000);
}, -1);
Task parent = workChain;
for (int i = 0; i < 10; i++)
{
parent = parent.ContinueWith((ante, o) =>
{
Console.WriteLine("Running task {0} with state {1}", Task.CurrentId, o);
Task subTask = Task.Factory.StartNew(() =>
{
Thread.Sleep(10000);
Console.WriteLine("Subtask completed");
});
subTask.Wait(cts.Token);
}, i, TaskContinuationOptions.OnlyOnRanToCompletion);
parent.ContinueWith((ante) =>
{
foreach (Exception e in ante.Exception.InnerExceptions)
{
if (e is OperationCanceledException)
{
//report timeout
Console.WriteLine("Timed out while running task id {0}", ante.Id);
return;
}
}
//report other exception
Console.WriteLine("Something bad happened: {0}", ante.Exception.GetBaseException());
}, TaskContinuationOptions.OnlyOnFaulted);
}
Task lastTask = parent.ContinueWith((ante) =>
{
//report success
Console.WriteLine("Success");
}, TaskContinuationOptions.OnlyOnRanToCompletion);