是否可以使用Wait来捕获ContinueWith中的异常?

Hed*_*ley 1 .net c# user-interface winforms task-parallel-library

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

我目前正在重新编写一些旧的异步代码例程,这些例程使用"MethodInvoker"/"AsyncCallback"(以保持UI流畅)与Tasks.

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

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) {...}
}
Run Code Online (Sandbox Code Playgroud)

好的 - 所以这是我到目前为止基于任务的等价物: -

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) {...}
}
Run Code Online (Sandbox Code Playgroud)

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

我试图让它尽可能简单,所以在ContinueWith方法中简单地调用" task.Wait() ",在那里捕获一个Exception(根据上面的代码)或者是否有更好的方法是可以接受的做法去做这个?

非常感谢!

更新和答案

为了澄清,我应该解释原始代码在WinForms应用程序中使用,目标是在长DB操作(或任何其他长进程)期间保持UI响应,通过在不同的线程上异步运行它们.

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);
}
Run Code Online (Sandbox Code Playgroud)

这个代码比" MethodInvoker "或" Task "方法至少有两个好处: -
(1)它更清晰
(2)Ater从await返回,后续代码在UI线程上,所以没有必要faff " if(this.InvokeRequired){...} "(请参阅Stephen Cleary的博客了解更多相关信息).

原来的问题怎么样?是否可以使用Wait来捕获ContinueWith中的异常?

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

Sil*_*gel 5

您可以用以下内容替换所有这些内容:

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

public async Task HandleLoadExceptions()
{  
    try
    {
        await LoadData();
    }
    catch (Exception E)
    {
        // handle exception
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Hedley DB操作*不需要任何线程*的工作.您不需要拥有一个线程,无论是UI线程还是任何其他线程,只是在DB工作时无所事事.就像你说你期待一封信,而你只有选择是整天坐在你的邮箱前等待这封信,或者因为你有其他你想做的事情,雇用一个人去整天坐在那里等你的邮箱给你的信.你根本不需要这样做.你不需要一个人坐在那里一整天都不做你的邮件. (4认同)
  • 谢谢Silas,虽然不会调用**等待LoadData();**在这种情况下是否同步? (2认同)
  • @SilasReinagel那也错了.`async`只允许在方法体中使用`await`关键字.它不会强制该方法是异步的,而是简单地允许特定的关键字(`await`)在*潜在*中使用,使其异步.同步方法可以标记为"async"(这是一件非常邪恶的事情,但你可以做到). (2认同)
  • @Hedley你没有*使用`Task.Run`在另一个线程中工作,但这是一种方法.既然你的工作不是CPU绑定的工作,你不需要另一个线程,你只需要发送你的网络请求并在它回来时做你的东西.你不需要一个线程.调度线程池线程只是为了让它在那里无所事事只是浪费系统资源. (2认同)
  • @Servy我认为你赢得了StackOverflow.这个比喻太神奇了! (2认同)