【问题标题】:Rethrowing exceptions in tasks c# without wait无需等待即可重新抛出任务c#中的异常
【发布时间】:2016-01-13 18:30:39
【问题描述】:

我有一个 GUI 应用程序,我想在其中运行任务中的某些内容,因此它不会保留 UI。我希望将任务中未处理的异常传播到应用程序级异常处理程序。 然而:

  1. 如果我只是在任务中抛出异常,它不会达到应用程序级别 除非我使用等待/等待,否则例外
  2. Async/Await - 我从 UI 构造函数调用该方法,所以我不能在那里使用 async/await,因为我需要继续构建。我只想运行任务并忘记。

我在考虑使用 dispatcher.invoke,你怎么看?

public MainWindow()
{
        InitializeComponent();

        MyMethodAsync();

        InitializeA();
        IntiializeB();
}

private void MyMethodAsync()
{
     Task t = Task.Run(() =>
     {
          //Do some stuff
          throw new Exception("Throwing some unexpected exception");
     }).ContinueWith(MyContinueWith);
}

private void MyContinueWith(Task task)
{
    if (task.IsFaulted && task.Exception != null)
    {
         dispatcher.BeginInvoke(new Action(() =>
         {
            throw task.Exception;
         }), null);
    }
}

【问题讨论】:

  • 按照惯例,后缀为“Async”的方法通常是返回已启动任务的方法。使用 async/await 关键字,这对调用者来说是自动处理的,实现并不重要。
  • @skippy: await 不会阻止您的用户界面。听起来await 正是您想要的行为。
  • 你为什么使用Task.ContinueWith?真的不推荐,你应该改用 C#5 await。仅调试潜力就让使用Task.continueWith 变得疯狂。
  • 感谢您的快速解答。我不想使用 await 的原因是我想在 UI 线程上运行的构造函数中使用 MyMethodAsync。所以我不要等待和等待我的窗口的建设。 (我不能使构造函数异步)
  • 我已经用这条评论编辑了问题

标签: c# exception task


【解决方案1】:

我能想到的两种方法。首先,注册TaskScheduler.UnobservedTaskException 事件并在那里记录您需要的任何内容:

private void MyMethodAsync()
{
    // Note you should probably register only once, so this may not fit here.
    TaskScheduler.UnobservedTaskException += (s, e) => GlobalLogger.Log(e);
    Task t = Task.Run(() =>
    {
        // Do some staff
    }).ContinueWith(MyContinueWith);
}

出于某种原因您不想使用的更好的选择是实际等待操作并将其包装在try-catch中:

private async Task MyMethodAsync()
{
    try
    {
       await Task.Run(() =>
       {
          // Do some staff
       });
       InvokeContinuation();
    }
    catch (Exception e)
    {
        // Log.
    }
}

【讨论】:

  • 嗨 Yuval,正如我在 -await- 遇到的问题之前评论的那样,我想在 UI 构造函数中使用 MyMethodAsync,并且我不想使用 await 来保留构造函数操作,我只想跑着忘记。
  • @skippy 然后选择第一个选项。
  • 我已经检查了第一个选项,但它似乎也没有帮助。只有当任务被 GC 收集时,它才会去事件(我不知道它什么时候会发生,除非我调用 GC.Collect,否则它不会到达我的示例中的处理程序)。我现在确实注意到,当它是 GC 时,异常将被抛出,并到达我的应用程序级异常处理程序,这很好,但我仍然想在异常发生时到达那里,而不是有时依赖于 GC
  • @skippy 那么你需要同步或异步等待。
  • 同步 - 我不能,因为它会保持我的用户界面,并且异步......?我该怎么做(没有回到同样的问题)
【解决方案2】:

请意识到,通过调用 Task.Run,​​您通常会生成一个新线程,而这在大多数情况下不太可能是您想要的。在某些情况下创建新线程是有意义的,因为您正在执行 CPU 密集型工作,在这些情况下,您需要考虑利用其他 Parallel computation libraries 来充分利用它。相反,如果您的工作受 I/O 限制,您应该能够一直使用异步调用。

为了等待异步方法调用的结果或异常冒泡到调用点,您始终可以将对 ContinueWith 的调用附加到异步方法返回的任务。如果您同时处理结果和任何可能的异常,那么 async/await 语义就可以很好地工作。但是请注意,在这些延续中执行的代码默认情况下可能不会在与原始线程相同的线程中执行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-07
    • 2018-07-07
    • 1970-01-01
    • 1970-01-01
    • 2016-01-19
    • 2019-11-14
    • 2014-10-30
    • 2011-10-04
    相关资源
    最近更新 更多