【问题标题】:Try Catch outside of: await Task.Run(()在以下范围内尝试 Catch:await Task.Run(()
【发布时间】:2026-02-17 23:55:01
【问题描述】:

try catch outside: await Task.Run(() => 有意义还是只在 await 内使用它们?

private async void Test()
{
     try
     {
         await Task.Run(() =>
         {
             try
             {
                  DoingSomething();
             }
             catch (Exception ex)
             {
                  log.Error(ex.Message);
             }
         });
      }
      catch (Exception ex)
      {
          log.Error(ex.Message);
      }
}

【问题讨论】:

  • 这完全取决于您如何处理错误。
  • 另外,千万不要写async void
  • 我打算在等待中捕获所有错误,但是在外面添加 Try Catch 是否更安全?
  • @SLaks async void 有什么问题(或者它与任何其他 void 方法有何不同)?

标签: c# .net try-catch async-await


【解决方案1】:

如果您在委托中处理Exception(在您的情况下仅用于记录目的),await 在正常情况下不会引发异常。这应该没问题。

private async Task Test()
{
         await Task.Run(() =>
         {
             try
             {
                  DoingSomething();
             }
             catch (Exception ex)
             {
                  log.Error(ex.Message);
             }
         });

}

但是,由于您是 awaiting Task,很可能在 Test 方法中会有一些 DoSomethingElse,这可能会受到 Task 的结果的影响 - 在这种情况下在await 周围有一个try/catch 也是有意义的。

private async Task Test()
{
     try
     {
         await Task.Run(() =>
         {
             try
             {
                  DoingSomething();
             }
             catch (SomeSpecialException spex)
             {
                  // it is OK to have this exception
                  log.Error(ex.Message);
             }
         });

         DoSomethingElse(); // does not run when unexpected exception occurs.
      }
      catch (Exception ex)
      {
          // Here we are also running on captured SynchronizationContext
          // So, can update UI to show error ....
      }
}

【讨论】:

  • 我在控制台应用程序中尝试了这个,并在 catch 中设置了一个中断,它不在主线程中,我再次抛出,没有任何反应。我希望主线程得到异常。我该怎么做?
  • @pogorman 控制台应用没有 SynchronizationContext,因此 await 之后的代码可以在任何线程上运行。如果您的实际应用程序是 WPF 或 WinForms,则会在主线程上引发异常
  • 控制台应用程序的最佳策略是什么?如果后台发生异常,我希望应用程序严重崩溃,以便默默地处理错误。谢谢。
  • 理想情况下,我希望应用程序因任何意外异常而严重崩溃。
  • @pogorman,这是默认行为,除非您的线程被标记为后台线程
【解决方案2】:

如果您传递给Task.Run 的委托引发异常,那么当您await 返回的任务时,您可以在Task.Run 之外捕获它。

您不应将await 视为一个块。没有“内部等待”之类的东西。相反,将await 视为一个接受单个参数的运算符(在本例中,Task.Run 返回的Task)。 Task.Run 将捕获其委托的异常并将它们放在返回的 Task 上;然后await 将传播该异常。

【讨论】:

  • 如果委托被标记为异步(即使委托中没有等待语句),您只能在外部捕获异常await Task.Run(async () => { throw new Exception("msg"); });
  • @Arvis:不,没有asyncTask.Run 中的情况下你可以捕捉到它。
  • 我刚刚遇到了同样的情况。我无法在Task.Run 之外捕获SQLException,除非我将async 关键字添加到我的lambda
  • @NonSecwitter 可能只有调试器在“未被用户代码处理”的异常上停止。
【解决方案3】:

您也可以将 try catch 添加到外部代码中。当异步调用期间发生异常时,编译器将执行 catch 部分。这里有更多详细信息,为什么你需要 try catch around await http://msdn.microsoft.com/en-us/library/vstudio/0yd65esw.aspx 看看异步方法中的异常

【讨论】:

    【解决方案4】:

    这是 .Net 6 中的行为:

    try{
     await Task.Run(()=>   & call whatever method    );
    }
    catch { handle the exception }          <-- the catch will never get hit. An unhandled exception in the Task.Run line will happen
    

    但是,这将起作用:

    try{
    await Task.Run(async ()=> & call some method );
    }
    catch(Exception ex){
       handle the exception
    }
    

    对于 .Net 6,()=> 之前的异步必须存在

    我刚刚确认了这一点。

    【讨论】: