取消时,连续链中的哪个任务正在运行?

时间:2013-10-10 15:32:02

标签: c# .net task-parallel-library

我有一个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任务 是循环中创建的最后一个任务的父级,当超时发生时,所有未完成的任务都被标记为已取消而未启动。

让每个任务检查令牌的状态并在其他地方存储一些东西在大多数情况下工作,但是在下一个任务开始之前一个任务完成后取消发生的可能性很小。在这种情况下,我没有发现任何关于第一个被取消的任务。我总是可以在任务开始时存储一些东西,如果它被取消则可以存储其他东西,但这很快就会感觉很快。

2 个答案:

答案 0 :(得分:0)

使每个可能正在运行的任务记录开始或停止执行。 TPL不记录何时运行的任务。

答案 1 :(得分:0)

我发现对我有用,但仍然感觉不干净:

  • 主链中的每个任务都不会使用CancellationToken,而是获得运行OnlyOnFaulted的第二个延续(不是主链的一部分)。
  • 每个任务都在子任务中完成,然后通过调用.Wait(CancellationToken)等待子任务。取消令牌时,Wait调用将引发异常,这会使正在运行的任务出现故障。这允许报告错误,而无需等待子任务完成。
  • 主链中的每个任务都运行OnlyOnRanToCompletion
  • 运行OnlyOnRanToCompletion的主链末尾的一个任务报告整个链的成功(即没有超时)。

取消令牌后,当前正在运行的任务将停止使用OperationCanceledException等待其子任务。该任务的side分支通过特殊处理前提OperationCanceledExceptionAggregateException.InnerExceptions的存在来处理异常(或任何其他异常)。由于主链中的任务出现故障,主链中的其他任务都不会运行。

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);