【问题标题】:Cancel all async methods if one throws an exception如果抛出异常,则取消所有异步方法
【发布时间】:2018-08-27 02:50:13
【问题描述】:

我使用取消令牌,我想了解它是如何工作的。我有两个异步方法(在我的例子中是两个,但理论上我可以有 100 个)。 如果其中一个引发异常,我想取消所有异步方法中的工作。

我的想法是在调用所有方法的异常中取消令牌。当一个令牌被取消时,我希望其他方法停止工作,但这并没有发生。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace CancelationTest
{
    class Program
    {
        static void Main(string[] args)
        {
            new Test();
            Console.ReadKey();
        }
    }

    public class Test
    {
        public Test()
        {
            Task.Run(() => MainAsync());
        }

        public static async Task MainAsync()
        {
            var cancellationTokenSource = new CancellationTokenSource();
            try
            {
                var firstTask = FirstAsync(cancellationTokenSource.Token);
                var secondTask = SecondAsync(cancellationTokenSource.Token);
                Thread.Sleep(50);
                Console.WriteLine("Begin");
                await secondTask;
                Console.WriteLine("hello");
                await firstTask;
                Console.WriteLine("world");
                Console.ReadKey();
            }
            catch (OperationCanceledException e)
            {
                Console.WriteLine("Main OperationCanceledException cancel");
            }
            catch (Exception e)
            {
                Console.WriteLine("Main Exception + Cancel");
                cancellationTokenSource.Cancel();
            }
        }

        public static async Task FirstAsync(CancellationToken c)
        {
            c.ThrowIfCancellationRequested();
            await Task.Delay(1000, c);
            Console.WriteLine("Exception in first call");
            throw new NotImplementedException("Exception in first call");
        }

        public static async Task SecondAsync(CancellationToken c)
        {
            c.ThrowIfCancellationRequested();
            await Task.Delay(15000, c);
            Console.WriteLine("SecondAsync is finished");
        }
    }
}

即使第一种方法抛出异常,第二种方法也会完成工作并将任务延迟 15 秒。

结果是什么:

开始

第一次调用异常

SecondAsync 完成

你好

主要异常 + 取消

我希望 secondAsync 停止延迟并抛出 OperationCancelException。 我希望得到这样的结果:

开始

第一次调用异常

主要异常 + 取消

主要操作CanceledException 取消

我在哪里犯错了? 为什么方法 SecondAsync 完全执行并且不抛出异常?如果我更改 SecondAsync 和 FirstAsync 的顺序,而不是 SecondAsync 方法在令牌被取消时停止延迟并引发异常。

【问题讨论】:

  • 合作取消:您的任务必须定期检查令牌,这需要循环。
  • @HenkHolterman OP 将CancellationToken 传递给Task.Delay。令牌发出信号时是否应该取消延迟?
  • @PetSerAl - 是的,我忽略了这一点。虽然文档对延迟取消的时间有点模糊,但我认为这里可能仍需要 15 秒。
  • @Raskolnikov Cancellation 无法按照您的预期“订阅令牌取消事件”。ThrowIfCancellationRequested 只需检查令牌取消 是否 请求,如果是 - 抛出异常,但如果不是 - 它什么也不做。后期取消也不会抛出。
  • 您也可以register a Delegate在请求取消时运行。

标签: c# async-await task


【解决方案1】:

因为你代码的相关部分是:

try
{
   ...
   await secondTask;
   await firstTask;
}
catch(...)
{
    source.Cancel();
}

现在,当 firstTask 启动并抛出时,它在 secondTask 之后等待。在等待任务之前,异常不会出现在调用者中。所以 catch 子句只会在 secondTask 已经完成后执行。 Cancel() 只是发生得太晚了。

如果你想让你的第一个任务中断第二个任务,你将不得不

  1. 将源传递到 FirstAsync 并在那里调用 Cancel()。有点丑。
  2. 更改等待结构。我认为你的样本有点人为。使用 Parallel.Invoke() 或类似的东西,它会很自然地发生。

【讨论】:

  • 这究竟如何回答 OPs 的问题。引用:'我希望其他方法停止工作,但这没有发生'?
  • 它完全回答了这个问题。并且紧凑。浏览代码。
  • 我的意思是答案太短了,如果你添加更多关于“为什么其他方法停止工作”的解释会很好......也许我错了,但恕我直言,OP想要找到一个回答为什么方法 SecondAsync 被完全执行并且不会在 ThrowIfCancellationRequested 中抛出异常
  • 我不知道 OP 对 ThrowIfCancellationRequested 的看法,这并不重要。
  • 如何使用Parallel.Invokeasync 方法?
【解决方案2】:

您的CancellationTokenSource 需要位于被调用方法可访问的范围内,并且您需要调用CancellationTokenSource.Cancel() 以取消使用该源的所有操作。

您也可以致电CancellationTokenSource.CancelAfter(TimeSpan) 延迟取消。

【讨论】:

  • 它在方法和主方法调用 Cancel() 的范围内
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-15
  • 2017-01-12
  • 1970-01-01
  • 2011-10-18
相关资源
最近更新 更多