[英]Which Task in a continuation chain was running when cancelled?
I have a continuation chain of Task
s that may be cancelled, say, after a timeout occurs. 我有一个
Task
的延续链,例如在发生超时之后,可以取消它。 I want to identify which task was running when the cancellation happened. 我想确定取消发生时正在运行的任务。 Here's what I'm talking about:
这就是我在说的:
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);
When I run the above, the antecedent passed into the OnlyOnCanceled
one isn't the task that was running when things were cancelled. 当我执行上述操作时,传递给
OnlyOnCanceled
的前提不是取消事情时正在运行的任务。 I know why: the OnlyOnCanceled
task is parented by the last task created in the loop, and when the timeout occurs, all tasks that aren't completed are marked as canceled without being started. 我知道为什么:
OnlyOnCanceled
任务由循环中创建的最后一个任务作为父项,并且当发生超时时,所有未完成的任务都标记为已取消而未启动。
Having each task check the state of the token and store something elsewhere works most of the time, but there's a small chance that the cancellation happens after one task completes before the next task begins. 在大多数情况下,让每个任务检查令牌的状态并在其他地方存储内容是可行的,但是在一个任务完成之后,下一个任务开始之前,取消的可能性很小。 In that case, I don't find out anything about the first canceled task.
在那种情况下,我什么也找不到关于第一个被取消任务的信息。 I could always store something when a task starts and something else if it is canceled, but this starts to feel kludgy pretty quickly.
我总是可以在任务开始时存储一些东西,而在任务被取消时可以存储其他东西,但这很快就会使人感到困惑。
Make each task that might be running record that it started or stopped executing. 使每个可能正在运行的任务记录其开始或停止执行的时间。 The TPL does not record which tasks were running when.
TPL不记录哪些任务在何时运行。
What I found works for me, but still doesn't feel as clean as it should: 我发现的内容对我来说很有效,但仍然感觉不如预期:
CancellationToken
, but gets a second continuation (not part of the main chain) that runs OnlyOnFaulted
. CancellationToken
,而是获得第二个继续执行(不是主链的一部分)的继续执行OnlyOnFaulted
。 .Wait(CancellationToken)
. .Wait(CancellationToken)
子任务。 When the token is canceled, the Wait call throws an exception, which faults the task that was running. OnlyOnRanToCompletion
. OnlyOnRanToCompletion
。 OnlyOnRanToCompletion
reports success of the entire chain (ie no timeout). OnlyOnRanToCompletion
的主链末尾的一项任务报告了整个链的成功(即没有超时)。 When the token is canceled, the currently-running task stops waiting for its child task with an OperationCanceledException
. 取消令牌后,当前正在运行的任务将停止等待带有
OperationCanceledException
子任务。 The side branch for that task handles the exception (or any other exception) by special-casing the presence of OperationCanceledException
in the antecedent's AggregateException.InnerExceptions
. 该任务的侧分支通过特殊处理前一个对象的
AggregateException.InnerExceptions
中OperationCanceledException
的存在来处理异常(或任何其他异常)。 Since the task in the main chain faulted, no other task in the main chain will run. 由于主链中的任务出现故障,因此主链中的其他任务将不会运行。
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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.