如果抛出异常,则取消所有异步方法

时间:2018-03-18 12:20:26

标签: c# async-await task

我玩取消令牌,我想了解这是如何工作的。我有两个异步方法(在我的例子中有两个,但理论上我可以有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。 我期待这个结果:

  

开始

     

第一次通话中的例外

     

主要例外+取消

     

Main OperationCanceledException取消

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

2 个答案:

答案 0 :(得分:3)

因为代码的相关部分是:

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

现在,当firstTask启动并抛出时,等待第二个任务之后。在等待任务之前,异常不会在调用者中浮出水面。因此catch子句只会在secondTask完成后执行。 Cancel()发生得太晚了。

如果你想让你的firstTask中断第二个,你必须

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

答案 1 :(得分:0)

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

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