我的视图模型中有一个方法
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来调用异步方法会创建一个新线程。
答案 0 :(得分:9)
他们不应该一样吗?我以为没有使用等待 调用异步方法会创建一个新线程。
不,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);
}
答案 1 :(得分:0)
你的行Task.Run(() => this.SyncContacts());
真正做的是创建一个新任务,启动它并将其返回给调用者(在你的情况下不用于任何其他目的)。这就是为什么它将在后台运行并且UI将继续工作的原因。如果您需要(a)等待任务完成,您可以使用await Task.Run(() => this.SyncContacts());
。如果您只想确保在返回SyncData方法时SyncContacts已完成,则可以使用返回的任务并在SyncData方法结束时等待它。正如评论中所建议的那样:如果你对任务是否完成不感兴趣,你就可以将其归还。
但是,Microsoft建议不要混用阻止代码和异步代码,并且async方法以Async(https://msdn.microsoft.com/en-us/magazine/jj991977.aspx)结尾。因此,在不使用await关键字时,应考虑重命名方法,不要使用async标记方法。
答案 2 :(得分:0)
只是为了澄清UI冻结的原因 - 在紧密find ./ -iname "*.jpg"
循环中完成的工作很可能是CPU绑定的,并且会阻塞原始调用者的线程,直到循环完成。
因此,无论foreach
返回的Task
是否为SyncContacts
,在调用await
之前的CPU绑定工作仍将同步发生,并且阻止,调用者的线程。
AddContactsAsync
(回复:为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的观点 - 让方法异步并等待结果将是wasteful in this instance)
对于WPF项目,可以使用Task.Run
从调用线程(但是not so for MVC or WebAPI Asp.Net项目)执行CPU绑定工作。
此外,假设contactsToUpload
映射工作是线程安全的,并且您的应用程序充分利用了用户的资源,您还可以考虑并行化映射以减少总体执行时间:
var contactsToUpload = this.AllContacts
.AsParallel()
.Select(contact => MapToUploadContact(contact));
// or simpler, .Select(MapToUploadContact);