【问题标题】:Exception in task not breaking immediately任务中的异常不会立即中断
【发布时间】:2013-03-07 00:09:07
【问题描述】:

一些伪代码来说明我的问题:

public async Task DoSomethingAsync()
{
   try
   {
      var task1 = DoThisAsync(); // throws exception
      var task2 = DoThatAsync();

      await task1.Then(t => Handle(t));
      await task2.Then(t => Handle(t));
   }
   catch (AggregateException)
   {
      Console.WriteLine("Whatnow?");
   }
}

And Then 是这样定义的:

// from https://gist.github.com/rizal-almashoor/2818038
public static Task Then(this Task task, Action<Task> next)
{ 
   var tcs = new TaskCompletionSource<AsyncVoid>();

   task.ContinueWith(t=>
   {
      if (t.IsFaulted)
         tcs.TrySetException(t.Exception); // continuing task1 this line only gets hit
                                           // after DoThatAsync() is completed??
      else
      {
         try
         {
            next(t);
            tcs.TrySetResult(default(AsyncVoid));
         }
         catch (Exception ex)
         {
            tcs.TrySetException(ex);
         }
      }
   });

   return tcs.Task;
}

所以我的问题是,由于某种原因,即使 DoThisAsync() 很早就抛出了异常,但在 DoThatAsync() 完成之前我看不到“whatnow”。 这不是确切的代码,我试图简化以免浪费您的时间。如果这里没有任何内容可以解释这种行为,请告诉我,我会添加更多细节。

编辑

出于这个问题的目的,我们可以想象 DoThisAsync() 和 DoThatAsync() 是异步方法,基本上执行以下操作:

DoThisAsync:
   Thread.Sleep(30000);    // wait a short perioud of time
   throw new Exception();  // and throw an exception

DoThatAsnyc:
   Thread.Sleep(240000);   // wait a long period of time

【问题讨论】:

  • 你能添加一个完整的例子吗,因为在我做的一个小例子中,这不是发生的事情,DoThatAsync 没有被调用...
  • 所以我编辑了我的问题,现在它看起来更像真正的代码。此外,确保 DoThisAsync() 在异常发生之前有足够长的时间(睡眠),例如一两分钟,以便有时间启动 DoThatAsync()
  • 这仍然不是一个完整的例子。请举例说明你在 DoThis/ThatAsync() 中所做的事情
  • 不确定你的意思,DoThisAsync() does 抛出一个我 do 捕获的异常,我唯一的问题是为什么我没有捕获它在 DoThatAsync() 完成之前?
  • 您的示例 DoThatAsync 是完全同步的。您的实际代码是同步的还是异步的? (完全同步的方法会同步运行,即使它被标记为async)。

标签: c# asynchronous task-parallel-library


【解决方案1】:

大概您的DoThisAsync 开始了一项新任务,而该任务的动作是引发异常的原因——对吗?

在这种情况下,异常存储在任务中。除非您调用像 .Wait 或 .Result 这样的触发器方法,否则不会重新引发异常。当您awaitThen 返回的任务时,它会导致该任务的异常被重新抛出。

编辑: 根据您显示 DoThisAsync 的编辑: 当返回 Task 的 async 标记方法导致异常时,该异常将存储在 Task 中(而不是允许它传播)。如果您要删除 async 关键字,我希望在调用 DoThisAsync 时会发生异常。

编辑: 来自 Stephen Toub 的 Async/Await 常见问题解答:http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/async-await-faq.aspx

“async”关键字应用于方法时有什么作用?

当您使用“async”关键字标记方法时,您实际上是在告诉编译器两件事:

  1. 您告诉编译器您希望能够在方法中使用“await”关键字(当且仅当它所在的方法或 lambda 标记为异步时,您才可以使用 await 关键字)。这样做时,您是在告诉编译器使用状态机编译该方法,以便该方法能够暂停,然后在等待点异步恢复。
  2. 您是在告诉编译器“提升”方法的结果或返回类型中可能出现的任何异常。对于返回 Task 或 Task 的方法,这意味着在方法中未处理的任何返回值或异常都将存储到结果任务中。对于返回 void 的方法,这意味着任何异常都会通过方法初始调用时当前的任何“SynchronizationContext”传播到调用者的上下文。

【讨论】:

  • 异常抛出和捕获都成功,问题是什么时候。它仅在 DoThatAsync() 完成后发生。换句话说,“if (t.IsFaulted) tcs.TrySetException(t.Exception);”这一行Then() 方法中只有在 DoThatAsnc() 完成后才会被命中。 DoThisAsync() 抛出异常后是否应该受到一点影响?
  • 没有。我希望它会从await task1.Then ... 行中抛出。
  • 我明白了,让我换个方式问这个问题,为什么 await task1.Then... 行只有在 DoThatAsync() 完成后才会被击中?这是一个异步方法,代码应该直接进入下一行不是吗?
  • 首先,正如斯蒂芬指出的那样,DoThisAsync 没有异步执行任何操作。 await task1.Then .. 行在运行之前等待 task1 的完成(尽管在您的情况下,task1 将始终在此时完成,因此此处的 await 也将同步运行)。但是,您最初的问题更多的是关于异常和任务。
  • DoThatAsync() is 是异步的,我应该想出一个做同样事情的异步示例。但是,您对这个答案的第一条评论指出了我的问题。我的伪代码中的问题是 await task2.Then 在 task1 完成之前永远不会被命中,即使它们彼此无关。我不明白这一点。似乎只要有一个 await 关键字,该方法就不会更进一步,直到该任务完成。我认为只要我们不使用结果,它就会走得更远。重要的是你为我指明了正确的方向。
猜你喜欢
  • 2020-09-08
  • 1970-01-01
  • 2013-09-28
  • 1970-01-01
  • 2018-03-12
  • 1970-01-01
  • 2023-02-02
  • 2013-05-31
  • 1970-01-01
相关资源
最近更新 更多