![](/img/trans.png)
[英]Difference between calling .Wait() on an async method, and Task.Run().Wait()
[英]Difference between calling an async method and Task.Run an async method
我的视图模型中有一个方法
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());
}
}
一切顺利。 他们不应该一样吗? 我在想,不使用等待调用异步方法会创建一个新线程。
他们不应该一样吗? 我在想,不使用等待调用异步方法会创建一个新线程。
不, async
不会神奇地为其方法调用分配新线程。 async-await
主要是关于利用自然的异步API,例如对数据库的网络调用或远程Web服务。
使用Task.Run
,将显式使用线程池线程执行委托。 如果您使用async
关键字标记方法,但不在内部await
任何内容,它将同步执行。
我不确定您的SyncContacts()
方法实际上是做什么的(因为您没有提供它的实现),但是将它本身标记为async
将不会给您带来任何好处。
编辑:
现在您已经添加了实现,我看到两件事:
您无需等待异步操作。 它需要看起来像这样:
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( https://msdn.microsoft.com/zh-cn/magazine/jj991977.aspx )结尾。 因此,当您不使用await关键字时,应该考虑重命名方法,并且不要使用异步标记方法。
只是为了阐明UI冻结的原因-在紧密的foreach
循环中完成的工作可能受CPU限制,并且将阻塞原始调用者的线程,直到循环完成。
因此,无论是否await
从SyncContacts
返回的Task
,调用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);
}
(Re:不,为什么async / return await
在SyncContacts
上async / return await
SyncContacts
请参见Yuval的要点-使方法异步并等待结果在这种情况下会很浪费 )
对于WPF项目,应该可以使用Task.Run
在调用线程Task.Run
完成CPU绑定工作(但对于MVC或WebAPI Asp.Net项目则不是这样 )。
另外,假设contactsToUpload
映射工作是线程安全的,并且您的应用程序充分利用了用户的资源,那么您还可以考虑并行化映射以减少总体执行时间:
var contactsToUpload = this.AllContacts
.AsParallel()
.Select(contact => MapToUploadContact(contact));
// or simpler, .Select(MapToUploadContact);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.