调用异步方法和调用Task之间的区别.运行一个异步方法
本文关键字:调用 异步方法 一个 区别 Task 之间 运行 | 更新日期: 2023-09-27 18:07:22
我的视图模型中有一个方法
private async void SyncData(SyncMessage syncMessage)
{
if (syncMessage.State == SyncState.SyncContacts)
{
this.SyncContacts();
}
}
private async Task SyncContacts()
{
foreach(var contact in this.AllContacts)
{
// do synchronous data analysis
}
// ...
// AddContacts is an async method
CloudInstance.AddContacts(contactsToUpload);
}
当我从UI命令调用SyncData
时,我正在同步一大块数据UI冻结。但是当我用这种方法调用SyncContacts
时
private void SyncData(SyncMessage syncMessage)
{
if (syncMessage.State == SyncState.SyncContacts)
{
Task.Run(() => this.SyncContacts());
}
}
一切正常。它们不应该是一样的吗?我在想,不使用await调用异步方法会创建一个新线程。
它们不应该是相同的吗?我在想不使用await for调用异步方法会创建一个新线程。
不,async
不会神奇地为它的方法调用分配一个新线程。async-await
主要是利用自然异步api,例如对数据库或远程web服务的网络调用。
当你使用Task.Run
时,你显式地使用线程池线程来执行委托。如果你用async
关键字标记一个方法,但在内部没有await
任何东西,它将同步执行。
我不确定你的SyncContacts()
方法实际上做了什么(因为你没有提供它的实现),但是把它标记为async
本身不会给你带来任何好处。
现在您已经添加了实现,我看到了两件事:
- 我不确定你的同步数据分析的CPU密集程度,但它可能足以让UI无响应。
您没有等待异步操作。它需要看起来像这样:
private async Task SyncDataAsync(SyncMessage syncMessage) { if (syncMessage.State == SyncState.SyncContacts) { await this.SyncContactsAsync(); } } private Task SyncContactsAsync() { foreach(var contact in this.AllContacts) { // do synchronous data analysis } // ... // AddContacts is an async method return CloudInstance.AddContactsAsync(contactsToUpload); }
您的Task.Run(() => this.SyncContacts());
行真正做的是创建一个新任务,启动它并将其返回给调用者(在您的情况下不用于任何其他目的)。这就是为什么它会在后台工作UI会继续工作。如果需要(a)等待任务完成,可以使用await Task.Run(() => this.SyncContacts());
。如果你只是想确保当你返回SyncData方法时SyncContacts已经完成,你可以使用返回任务并在SyncData方法结束时等待它。正如评论中建议的那样:如果你对任务是否完成不感兴趣,你可以返回它。
但是,Microsoft建议不要将阻塞代码和async代码混合使用,并且async方法以async结束(https://msdn.microsoft.com/en-us/magazine/jj991977.aspx)。因此,当你不使用await关键字时,你应该考虑重命名你的方法,不要用async标记方法。
只是为了澄清为什么UI会冻结-在紧密的foreach
循环中完成的工作可能是cpu限制的,并且会阻塞原始调用者的线程,直到循环完成。
所以,不管从SyncContacts
返回的Task
是否为await
ed,在调用AddContactsAsync
之前的CPU绑定工作仍然会同步地发生在调用者的线程上,并阻塞调用者的线程。
private Task SyncContacts()
{
foreach(var contact in this.AllContacts)
{
// ** CPU intensive work here.
}
// Will return immediately with a Task which will complete asynchronously
return CloudInstance.AddContactsAsync(contactsToUpload);
}
(回复:不知道为什么async / return await
在SyncContacts
上-参见Yuval的观点-在这种情况下,使方法异步并等待结果将是浪费)
对于WPF项目,使用Task.Run
在调用线程之外执行CPU绑定工作应该是可以的(但对于MVC或WebAPI Asp不是这样)。网项目)。
同样,假设contactsToUpload
映射工作是线程安全的,并且你的应用程序充分利用了用户的资源,你也可以考虑并行化映射以减少总体执行时间:
var contactsToUpload = this.AllContacts
.AsParallel()
.Select(contact => MapToUploadContact(contact));
// or simpler, .Select(MapToUploadContact);