【问题标题】:Task Exception Handling without Wait无需等待的任务异常处理
【发布时间】:2016-11-07 17:31:36
【问题描述】:

使用任务时,如果我不调用等待我的任务,我不确定如何处理。下面的例子不是在异步方法中执行的。

这是一个例子:

var t = Task.Run(() =>
{
  // do something as part of the task concurrently
});

将整个块包裹在上面并捕获异常是正确的方式吗?

我知道我可以等待下面的任务结果并处理异常,但我的问题与上面的块有关,无需调用 t.Wait。

try
{
  t.Wait();  
}
catch(AggregateException ae)
{
  // handle exception(s)
}

所以,我的问题是,当我不等待(或等待)任务时,这是否是处理异常的正确方法?

try
{
  var t = Task.Run(() =>
  {
    // do something as part of the task concurrently
  });
}
catch(Exception ex) //NOTE general Exception
{
  // show exception in message box or log it somewhere
}

更新1 ,还是我应该这样做?

  var t = Task.Run(
      () => 
      {
        try
        {
          // do something as part of the task concurrently
        }
        catch(Exception ex) //NOTE general Exception
        {
          // show exception in message box or log it somewhere
        }
       });

【问题讨论】:

  • 不要选择你的单词选择,“//异步做某事作为任务的一部分”应该是“//做某事作为任务的一部分同时”。使用 Task.Run 不会使某些东西异步,您需要 API 支持才能真正异步。
  • 到您的最新更新,不,它不正确。想一想,如果您不等待 t 并且需要“10 行代码的时间”来抛出异常,那么您现在就退出了 try 块。
  • 回复:更新 1 - 当然可以。这意味着您的错误处理在任务内部,这意味着它不知道是任务或任务运行的上下文。如果这就是你所追求的,那就去吧。这与任何其他方法中的异常处理没有什么不同。
  • 然后回答这个问题 - 所有方法都应该处理自己的异常,还是应该向调用者抛出异常。如果是后者,您需要以下答案中的解决方案之一。l
  • 你的 update1 也是错误的,尝试需要在 () => 之后而不是之前。

标签: c# exception-handling task


【解决方案1】:

您提供的代码(在编辑之后)不会帮助您处理您的任务中抛出的错误。它只会捕获主代码块中抛出的异常,例如在调度任务或获取要传递给它的参数时。

Async/Await 简化异步任务控制流程

如果您使用的是 C# 5 及更高版本(与 VS2013 捆绑),最简单的方法是使用 async/await,它可以简化异步任务的控制流程:

public async Task DoSomething()
{
    try
    {
        DoSyncWork();
        await Task.Run(() => AsyncStuff());
    }
    catch (Exception ex)
    {  
        // handle.
    }
}

编译器将自动解构异步任务并将异常返回到正常的catch 块 - 无论代码的同步或异步部分是否引发了异常。

OnlyOnFaulted 延续处理任务异常

如果您不使用 C# 5,或者不想使用 async/await(因为您正在处理并行性,而不仅仅是异步),则该技术是使用 Task.ContinueWith 来指定在发生错误时的延续:

var task = Task.Run(() => Whatever())
               .ContinueWith(failedTask => HandleError(failedTask), 
                             TaskContinuationOptions.OnlyOnFaulted);

这将导致在原始任务完成后继续启动,但前提是它引发了异常。您可以使用它来根据结果指定几个延续分支:

var baseTask = Task.Run(() => Whatever());            
baseTask.ContinueWith(failedTask => HandleError(failedTask), 
                             TaskContinuationOptions.OnlyOnFaulted);
baseTask.ContinueWith(successfulTask => HandleResults(successfulTask), 
                             TaskContinuationOptions.OnlyOnRanToCompletion);

【讨论】:

  • 但我不希望方法 DoSomething() 成为异步方法。而且我不想使用等待或等待。我更新了我的问题代码以澄清
  • @dbnex14 然后使用 contunue 或将 try/catch 放在 Task.Run 内部而不是外部。
  • @dbnex14 然后继续阅读答案的第二部分。我添加了部分标题以澄清。
  • @ScottChamberlain 谢谢,我在上面添加了 UPDATE1。这就是你建议的“......将try / catch放在Task.Run而不是它之外。”?再次感谢
  • @dbnex14 正如我在 cmets 中对答案所说,这意味着您的任务将始终成功完成,无论它是否实际失败。任务的调用者无法知道是哪个。
【解决方案2】:

如果没有等待,在 try/catch 块中运行任务将永远不会捕获异常。任务在独立于 try/catch 逻辑的自己的线程上启动,因此程序的调用部分会继续,除非它被指示等待任务完成。

如果您希望它记录或处理错误,请将该逻辑构建到任务本身中。

【讨论】:

    猜你喜欢
    • 2014-06-30
    • 2018-01-15
    • 1970-01-01
    • 1970-01-01
    • 2013-09-22
    • 2020-12-21
    • 1970-01-01
    • 1970-01-01
    • 2016-01-13
    相关资源
    最近更新 更多