2

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

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

4

2 回答 2

0

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

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

当令牌被取消时,当前运行的任务将停止等待其子任务的OperationCanceledException. 该任务的侧分支通过对前件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);
于 2013-10-11T15:04:16.070 回答
0

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

于 2013-10-10T16:26:20.433 回答