c# - 在方法签名中使用异步时,任务取消异常不会冒泡

标签 c# multithreading task-parallel-library task

我不太明白这个。在下面的代码中,按原样运行, token.ThrowIfCancellationRequested() 引发的异常不会被发送回 Main 使用 .Wait() 的位置,而是当场抛出,堆栈跟踪并不真正知道它发生在哪里除了“任务被取消”之外。但是,如果我删除 async 关键字并使用 wait Task.Delay() 删除 try catch block ,它确实会被发送回 main 中的 .Wait() 并在那里被捕获。

我做错了什么吗,或者我到底如何让await Task.Delay()和token.ThrowIfCancellationRequested()抛出的异常都冒泡到.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方法。

async void 方法在概念上是事件处理程序,因此如果它抛出异常,它将直接在其 SynchronizationContext 上引发 - 在本例中,在线程池上引发线程。

void 返回方法的异步等效项不是 async void 返回方法;它是一个 async Task 返回方法。因此,您的 Monitor 方法应返回 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标志;这只是一种优化,没有它线程池也能正常工作。

您可能会找到我的async/await introofficial MSDN documentation有帮助。

关于c# - 在方法签名中使用异步时,任务取消异常不会冒泡,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13696791/

相关文章:

c# - 无法为文件夹设置完全控制权限

c# - 如何快速有效地从 'simple' 截图中读取文本?

c# - 列出超过 8 个项目的元组

java - BufferedReader 链接到套接字,readLine() != null 是如何工作的?

c# - 如何在 cosmos db C# 中插入一个文档的项目列表?

c# - 一种在子线程被杀死或中断时通知父线程的方法

c# - 写入文件系统。如何有效锁定

c# - 为什么不在 ContinueWith 中执行任务?

c# - ParallelOptions.CancellationToken 似乎没用

c# - Task.WaitAll 和取消的任务