了解异步方法

本文关键字:异步方法 了解 | 更新日期: 2023-09-27 18:21:27

我有一个方法void getInformation(),它调用其他5个方法,每个方法都从数据库中获取一些数据。大约需要1秒的时间才能收集到所有数据并返回到getInformation(),因此我认为我应该在后台收集数据。我的问题是:我可以只将getInformation()设置为async,这样在其他方法收集信息时UI就不会被阻止吗?还是必须将其他所有方法都设置为async

private void button_Click(object sender, EventArgs e)
{
  await getContactInformation();
}
public async Task getContactInformation()
{
  this.data.Add(collectData1());
  this.data.Add(collectData2());
  this.data.Add(collectData3());
  this.data.Add(collectData4());
  this.data.Add(collectData5());
}

了解异步方法

首先,async的理想情况是使用"async all way"。在这种情况下,您的代码将如下所示:

private async void button_Click(object sender, EventArgs e)
{
  await getContactInformationAsync();
}
public async Task getContactInformationAsync()
{
  this.data.Add(await collectData1Async());
  this.data.Add(await collectData2Async());
  this.data.Add(await collectData3Async());
  this.data.Add(await collectData4Async());
  this.data.Add(await collectData5Async());
}

请注意,现代数据库API,如EntityFramework6、ADO.NET、SQLite等,都有异步API,可以用来实现collectDataNAsync方法。这是理想的async场景,因为它最大限度地减少了线程浪费,并使UI保持良好的响应性。

然而,如果您想要更多的"快速修复",那么您可以将(同步)调用推送到后台线程,然后异步处理后台工作,例如:

private async void button_Click(object sender, EventArgs e)
{
  await Task.Run(() => getContactInformation());
}
public void getContactInformation()
{
  this.data.Add(collectData1());
  this.data.Add(collectData2());
  this.data.Add(collectData3());
  this.data.Add(collectData4());
  this.data.Add(collectData5());
}

这将以牺牲线程池线程为代价来解决眼前的问题(释放UI线程)。这不是理想的,但它会起作用。

通常,我建议将所有IO从UI线程中删除,但对每个单独的调用async没有意义。

对于async调用对数据库IO阻塞线程的影响的指导,我发现了这个非常清晰和有用的建议。

http://blogs.msdn.com/b/rickandy/archive/2009/11/14/should-my-database-calls-be-asynchronous.aspx

否。您的方法体必须使用等待,否则将收到警告。

很简单。

用…包裹你的方法体。。。

await Task.Run(()=>{
// do your thing
});

我可以让getInformation()异步,这样在其他方法收集信息时UI就不会被阻塞吗?还是必须让其他所有方法异步?

后者。当您执行async时,它是"async all way",这意味着您将async方法调用一直进行到堆栈的顶部。对于IO绑定操作,不需要使用后台线程,因为现在大多数API都公开了真正异步的XXXAsync方法调用,这意味着在执行IO请求时,UI消息循环可以自由处理其他消息。

不过,需要注意的一点是,标记方法async不足以使其异步。实际上,您必须await一个异步操作,例如数据库方法调用。如果没有对数据库的异步API调用,您将无法进行异步。

在您的情况下,getContactInformation将使编译器省略一个警告,说明您有一个未等待的异步方法。事实上,如果你await getInformation(),你将完全同步运行。