【问题标题】:Is it ok to use Wait to catch Exceptions in ContinueWith?在 ContinueWith 中使用 Wait 捕获异常可以吗?
【发布时间】:2017-02-24 18:47:10
【问题描述】:

我刚刚将我的主应用程序从 .NET 2.0 升级到 .NET 4.5,我正在尝试利用 C# 5.0 提供的更高级的语言功能。

我目前正在考虑用 Tasks 重写一些使用“MethodInvoker”/“AsyncCallback”(以保持 UI 流畅)的旧异步代码例程。

这是旧代码遵循的模式...

void RefreshScreen()
{
  // code to prepare UI for updating
  ...
  MethodInvoker del = new MethodInvoker(LoadData);
  del.BeginInvoke(new AsyncCallback(LoadData_Callback), null);
}

void LoadData()
{
  // perform data calculations
}

void LoadData_Callback(IAsyncResult ar)
{
  AsyncResult res = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
  MethodInvoker del = (MethodInvoker)res.AsyncDelegate;
  try
  {
    del.EndInvoke(ar);  // <- throw exception if one occurred during async operation
  }
  catch (Exception E)
  {
    // handle exception
  }

  // Update the UI: if (this.InvokeRequired) {...}
}

好的 - 这是我目前拥有的基于任务的等效项:-

void RefreshScreen()
{
  // code to prepare UI for updating
  ...
  Task.Run(() => LoadData()).ContinueWith((t) => LoadData_Callback(t));
}

void LoadData()
{
  // perform data calculations
}

void LoadData_Callback(Task t)
{
  try
  {
    t.Wait(); // <- throw exception if one occurred during async operation
  }
  catch (Exception E)
  {
    // handle exception
  }

  // Update the UI: if (this.InvokeRequired) {...}
}

所以这是我的问题...我已经阅读了很多关于任务异常处理的文章和资源,其中一些讨论了诸如检查“task.IsCompleted”和/或“ task.Status”,以及使用 TaskContinuationOptions 分支成多个 ContinueWiths

我试图让它尽可能简单,所以在 ContinueWith 方法中简单地调用“task.Wait()”来捕获异常是否可以接受(根据上面的代码)还是有更好的方法来做到这一点?

非常感谢!

更新和回答

为了澄清起见,我应该解释一下原始代码用于 WinForms 应用程序,目标是通过异步运行它们来保持 UI 在长时间的 DB 操作(或任何其他长时间的过程)期间响应不同的线程。

Silas 和 Servy 指出,自从切换到 C# 5.0,我应该使用 async/await,所以这是我选择使用的类似代码对上面的例子...

async Task RefreshScreen()
{
  // code to prepare UI for updating
  ...
  try
  {
    await Task.Run(() => LoadData());
  }
  catch (Exception E)
  {
    // handle exception
  }
  // Update the UI: WITHOUT the need to check "this.InvokeRequired"
  return Task.FromResult(0);
}

Task LoadData()
{
  // perform data calculations
  return Task.FromResult(0);
}

与“MethodInvoker”或“Task”方法相比,此代码至少有两个好处:-
(1) 更清晰
(2) Ater从await返回,后面的代码在UI线程上,所以不用折腾"if (this.InvokeRequired) {...}" (see Stephen Cleary's blog for more information on this)。

原来的问题呢? ContinueWith中使用Wait来捕捉异常可以吗?

我想这是一个有争议的问题,因为 async/await 显然是要走的路,但经过进一步研究,答案是肯定的,可以这样做!

【问题讨论】:

  • 如果您使用的是 C# 5.0 功能,为什么不等待任务。 await 就像 C# 5.0 功能。
  • 这可能是一个很好的问题...我正在努力通过 O'Reilly 的 C# 6.0 in a Nutshell 并且还没有达到异步/等待的程度,但我“痒”了,想开始使用我学到的关于Tasks的知识!就是说 - 我刚刚看过那个主题,并不太明白如何以与上述类似的方式使用 async/await。 :-/

标签: c# .net winforms user-interface task-parallel-library


【解决方案1】:

你可以用这个替换所有的东西:

public async Task LoadData()
{
  // do something DB intensive
}

public async Task HandleLoadExceptions()
{  
    try
    {
        await LoadData();
    }
    catch (Exception E)
    {
        // handle exception
    }
}

【讨论】:

  • 感谢 Silas,虽然在那种情况下对 await LoadData(); 的调用不是同步的吗?
  • @SilasReinagel 这也是错误的。 async 只允许在方法体中使用 await 关键字。它不强制方法异步,而是简单地允许在 可能 中使用特定关键字 (await) 使其异步。同步方法可以标记为async(这样做是非常邪恶的事情,但你可以做到)。
  • @Hedley 您没有必须使用Task.Run 在另一个线程中工作,但这是一种方法。由于您的工作不是受 CPU 限制的工作,因此您不需要另一个线程,您只需要发送您的网络请求并在它返回时执行您的工作。你不需要一个线程。调度一个线程池线程只是让它坐在那里什么都不为你做只是浪费系统资源。
  • @Hedley DB 操作不需要任何线程的工作。你不需要有一个线程,无论是 UI 线程还是任何其他线程,只要在 DB 工作时坐在那里什么都不做。这就像你说你在等一封信,而你唯一的选择就是整天坐在邮箱前等待这封信,或者既然你有其他事情想做,那就雇一个人来整天坐在你的邮箱旁等着给你信。你只是不需要这样做。您不需要有人整天坐在那里无所事事地收邮件。
  • @Servy 我认为你赢得了 StackOverflow。这个比喻太棒了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-24
  • 2021-08-25
  • 2012-05-22
  • 2010-09-14
  • 2019-09-30
  • 1970-01-01
  • 2017-01-04
相关资源
最近更新 更多