猿问

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

我在寻找一个解释,而不仅仅是一个解决方案。我已经知道了解决方案。


尽管花了几天时间研究有关基于任务的异步模式(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冻结?任务对象有问题吗?有没有明显的僵局?


MMMHUHU
浏览 847回答 3
3回答

凤凰求蛊

async从同步代码中调用代码可能非常棘手。我在博客上解释了造成这种僵局的全部原因。简而言之,每个上下文的开头都有一个默认保存的“上下文”,await用于恢复该方法。因此,如果在UI上下文中调用await此async方法,则在完成时,该方法将尝试重新输入该上下文以继续执行。不幸的是,使用Wait(或Result)的代码将在该上下文中阻塞线程,因此该async方法无法完成。避免这种情况的准则是:使用ConfigureAwait(continueOnCapturedContext: false)尽可能多地。这使您的async方法可以继续执行而不必重新输入上下文。一直使用async。使用await代替Result或Wait。如果您的方法自然是异步的,那么(可能)您不应该公开同步包装器。
随时随地看视频慕课网APP
我要回答