【问题标题】:Which Task in a continuation chain was running when cancelled?取消时,延续链中的哪个任务正在运行?
【发布时间】:2013-10-10 15:32:01
【问题描述】:

我有一个Tasks 的延续链,例如,在发生超时后可能会被取消。我想确定取消发生时正在运行的任务。这就是我要说的:

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 任务 由循环中创建的最后一个任务作为父项,当发生超时时,所有未完成的任务都被标记为已取消而不开始。 p>

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

【问题讨论】:

  • 所以,你说你想知道取消发生时哪个任务正在运行,但正如你所说,它可能没有。取消可以在一个结束之后和下一个开始之前发生。您想知道最后一个成功完成的任务,还是第一个没有成功完成的任务?
  • 旁注,如果您希望 CTS 在 5000 毫秒内取消任务,只需将 5000 放入 CTS 构造函数中即可;比你正在做的要简单得多。
  • 你为什么用ContinueWith()而不是await?你为什么要使用Tasks 链而不是带有循环的单个Task
  • 我想知道超时前没有完成的第一个任务。这应该是较早的(在链中):取消令牌时正在运行的任务或由于令牌在任务开始之前取消而未启动的第一个任务。
  • 我无法创建一个包含循环的任务,因为(与上面的简化示例不同),所有工作可能不会在一个地方定义。而且我真的不想有一个Actions 或Funcs 的列表,然后创建一个任务来执行该列表中的所有内容。

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


【解决方案1】:

让每个可能正在运行的任务记录它开始或停止执行的情况。 TPL 不会记录哪些任务在何时运行。

【讨论】:

    【解决方案2】:

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

    • 主链中的每个任务都不会采用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);
    

    【讨论】:

      猜你喜欢
      • 2022-12-07
      • 1970-01-01
      • 2012-04-12
      • 2021-12-27
      • 1970-01-01
      • 1970-01-01
      • 2022-07-11
      • 1970-01-01
      • 2019-07-10
      相关资源
      最近更新 更多