为了账号安全,请及时绑定邮箱和手机立即绑定

同步等待异步操作,为什么Wait()在这里冻结程序

同步等待异步操作,为什么Wait()在这里冻结程序

MMMHUHU 2019-11-05 10:25:52
我在寻找一个解释,而不仅仅是一个解决方案。我已经知道了解决方案。尽管花了几天时间研究有关基于任务的异步模式(TAP),异步和等待的MSDN文章,但我对某些更详细的信息仍然感到困惑。我正在为Windows Store Apps编写记录器,并且希望同时支持异步和同步记录。异步方法遵循TAP,同步方法应该隐藏所有这些内容,并且外观和工作方式与普通方法类似。这是异步日志记录的核心方法:private async Task WriteToLogAsync(string text){    StorageFolder folder = ApplicationData.Current.LocalFolder;    StorageFile file = await folder.CreateFileAsync("log.log",        CreationCollisionOption.OpenIfExists);    await FileIO.AppendTextAsync(file, text,        Windows.Storage.Streams.UnicodeEncoding.Utf8);}现在对应的同步方法...版本1:private void WriteToLog(string text){    Task task = WriteToLogAsync(text);    task.Wait();}看起来正确,但是不起作用。整个程序永久冻结。版本2:嗯..也许任务没有开始?private void WriteToLog(string text){    Task task = WriteToLogAsync(text);    task.Start();    task.Wait();}这抛出 InvalidOperationException: Start may not be called on a promise-style task.版本3:嗯.. Task.RunSynchronously听起来很有希望。private void WriteToLog(string text){    Task task = WriteToLogAsync(text);    task.RunSynchronously();}这抛出 InvalidOperationException: RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.版本4(解决方案):private void WriteToLog(string text){    var task = Task.Run(async () => { await WriteToLogAsync(text); });    task.Wait();}这可行。因此,2和3是错误的工具。但是1?1有什么问题,与4有什么区别?是什么导致1冻结?任务对象有问题吗?有没有明显的僵局?
查看完整描述

3 回答

?
凤凰求蛊

TA贡献1825条经验 获得超4个赞

async从同步代码中调用代码可能非常棘手。


我在博客上解释了造成这种僵局的全部原因。简而言之,每个上下文的开头都有一个默认保存的“上下文”,await用于恢复该方法。


因此,如果在UI上下文中调用await此async方法,则在完成时,该方法将尝试重新输入该上下文以继续执行。不幸的是,使用Wait(或Result)的代码将在该上下文中阻塞线程,因此该async方法无法完成。


避免这种情况的准则是:


使用ConfigureAwait(continueOnCapturedContext: false)尽可能多地。这使您的async方法可以继续执行而不必重新输入上下文。

一直使用async。使用await代替Result或Wait。

如果您的方法自然是异步的,那么(可能)您不应该公开同步包装器。


查看完整回答
反对 回复 2019-11-05
  • 3 回答
  • 0 关注
  • 803 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信