在方法签名中使用异步时,任务取消异常不会冒泡 [英] Task cancellation exception not bubbling up when using async in method signature

查看:90
本文介绍了在方法签名中使用异步时,任务取消异常不会冒泡的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不太了解这一点.在我下面的代码中,按原样运行,令牌引发的异常.ThrowIfCancellationRequested()不会被发送回Main使用.Wait()的位置,而是与stacktrace一起当场抛出,并不真正知道它发生的位置除了任务已取消".但是,如果我删除async关键字并使用await Task.Delay()删除try catch块,它的确会发送回main中的.Wait()并在那里被捕获.

I don't really understand this. In my code below, run as-is, the exception thrown by token.ThrowIfCancellationRequested() is not being sent back to where Main is using .Wait(), and is instead thrown on the spot with the stacktrace not really knowing where it happened other than "A task was cancelled". However if I remove the async keyword and remove the try catch block with the await Task.Delay(), it does get sent back to .Wait() in main and is caught there.

我是在做错什么,还是我究竟是怎么得到等待Task.Delay()和token.ThrowIfCancellationRequested()引发的异常,两者都冒泡到.Wait()?

Am I doing something wrong, or how exactly do I get both the exception thrown by await Task.Delay() and token.ThrowIfCancellationRequested() to both bubble up to .Wait()?

static void Main(string[] args)
{
    var t = new CancellationTokenSource();
    var task = Task.Factory.StartNew((x) => Monitor(t.Token), t.Token, TaskCreationOptions.LongRunning);

    while (true)
    {
        if (Console.ReadLine() == "cancel")
        {
            t.Cancel();
            try 
            {
                task.Wait();
            }
            catch(AggregateException)
            {
                Console.WriteLine("Exception via task.Wait()");

            }
            Console.WriteLine("Cancelled");
        }
    }
}

static async void Monitor(CancellationToken token)
{
    while(true)
    {
        for(int i = 0; i < 5; i++)
        {
            // If the async in the method signature, this does not send the exception to .Wait();
            token.ThrowIfCancellationRequested();

            // Do Work
            Thread.Sleep(2000);
        }

        // Wait 10 seconds before doing work again.

        // When this try block is removed, and the async is taken out of the method signature,
        // token.ThrowIfCancellationRequested() properly sends the exception to .Wait()
        try
        {
            await Task.Delay(10000, token);
        } 
        catch(TaskCanceledException) 
        {
            Console.WriteLine("Exception from Delay()");
            return;
        }
    }
}

推荐答案

您应避免使用async void.它的异常处理语义很棘手,并且不可能组合到其他async方法中.

You should avoid async void. Its exception handling semantics are tricky, and it's not possible to compose into other async methods.

async void方法在概念上是事件处理程序,因此,如果引发异常,它将直接在其SynchronizationContext上引发-在这种情况下,将引发在线程池线程上.

async void methods are conceptually event handlers, so if it throws an exception, it will be raised directly on its SynchronizationContext - in this case, thrown on a thread pool thread.

void-返回方法的异步等效项不是async void-返回方法.这是async Task返回方法.因此,您的Monitor方法应返回Task:

The asynchronous equivalent of a void-returning method is not an async void-returning method; it is an async Task-returning method. So your Monitor method should return Task:

static void Main(string[] args)
{
  var t = new CancellationTokenSource();
  var task = Monitor(t.Token);

  while (true)
  {
    if (Console.ReadLine() == "cancel")
    {
      t.Cancel();
      try 
      {
        task.Wait();
      }
      catch(AggregateException)
      {
        Console.WriteLine("Exception via task.Wait()");
      }
      Console.WriteLine("Cancelled");
    }
  }
}

static async Task Monitor(CancellationToken token)

不必担心会丢失LongRunning标志;这只是一个优化,没有它,线程池也可以正常工作.

Don't worry about missing the LongRunning flag; it's just an optimization and the thread pool will work fine without it.

您可能会找到我的 async/await简介官方MSDN文档有用.

You may find my async/await intro or the official MSDN documentation helpful.

这篇关于在方法签名中使用异步时,任务取消异常不会冒泡的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆