问题描述
我试图在应用程序启动时加载和读取设置文件,大约 90% 的时间,await GetFileAsync("filename.xml");
永远不会返回,因此,挂起应用程序.
I'm attempting to load and read a settings file on application launch, and about 90% of the time, the await GetFileAsync("filename.xml");
never returns, thus, hanging the application.
大约四分之一的时间,如果我单步执行代码,它实际上会返回并读取文件.
About a quarter of the time, if I step through the code, it'll actually return and read the file.
这是一个非常简化的代码:
Here's a very simplified version of the code:
App.xaml.cs:
App.xaml.cs:
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
FileLoader.Load().Wait();
// File-load dependent stuff
}
FileLoader.cs:
FileLoader.cs:
public async static Task Load()
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file;
bool fileExists = true;
try
{
// The following line (often) never returns
file = await folder.GetFileAsync("filename.xml");
{
catch
{
fileExists = false;
}
// Do stuff with loaded file
}
如果我在 Visual Studio 中观看输出"窗口,等待一段时间后,我得到 "The thread '<No Name>'(0x30c) 已退出,代码为 0 (0x0)."
If I watch the Output window in Visual Studio, after awhile of waiting I get "The thread '<No Name>' (0x30c) has exited with code 0 (0x0)."
有人知道这里发生了什么吗?
Does anyone have any idea of what's happening here?
推荐答案
默认情况下,当你 await
一个尚未完成的 Task
时,方法会在捕获的上下文(在本例中为 UI 上下文).
By default, when you await
a Task
that has not yet completed, the method resumes on a captured context (in this case, the UI context).
所以,这就是您的代码失败的原因:
So, here's why your code is failing:
OnLaunched
调用Load
(在 UI 上下文中).加载
等待.这会导致Load
方法返回一个未完成的任务并安排其完成以供以后使用.此延续计划针对 UI 上下文.OnLaunched
阻塞从Load
返回的任务.这会阻塞 UI 线程.GetFileAsync
最终完成,并尝试运行Load
的延续.Load
的延续等待 UI 线程可用,以便它可以在 UI 上下文中执行.- 此时,
OnLaunched
正在等待Load
完成(这样做会阻塞 UI 线程),而Load
正在等待UI 线程是空闲的.死锁.
OnLaunched
callsLoad
(within the UI context).Load
awaits. This causes theLoad
method to return an incomplete task and schedule its completion for later. This continuation is scheduled for the UI context.OnLaunched
blocks on the task returned fromLoad
. This blocks the UI thread.GetFileAsync
eventually completes, and attempts to run the continuation forLoad
.- The continuation for
Load
waits for the UI thread to be available so it can execute in the UI context. - At this point,
OnLaunched
is waiting forLoad
to complete (blocking the UI thread by doing so), andLoad
is waiting for the UI thread to be free. Deadlock.
这些最佳做法可以避免这种情况:
These best practices avoid this situation:
- 在您的库"
async
方法中,尽可能使用ConfigureAwait(false)
.在您的情况下,这会将await folder.GetFileAsync("filename.xml");
更改为await folder.GetFileAsync("filename.xml").ConfigureAwait(false);
. - 不要阻塞
Task
s;它一直是async
.换句话说,将Wait
替换为await
.
- In your "library"
async
methods, useConfigureAwait(false)
whenever possible. In your case, this would changeawait folder.GetFileAsync("filename.xml");
toawait folder.GetFileAsync("filename.xml").ConfigureAwait(false);
. - Don't block on
Task
s; it'sasync
all the way down. In other words, replaceWait
withawait
.
更多信息:
- 等待,UI,还有死锁!天哪!
- 我的
async
/await
简介帖子,其中简要介绍了Task
等待者如何使用SynchronizationContext
,并介绍了一些最佳实践. - Async/Await 常见问题解答,更详细在上下文中.
- 此MSDN 论坛帖子.
- Stephen Toub 演示了这个死锁,以及Lucian Wischik也是如此.
- Await, and UI, and deadlocks! Oh, my!
- My
async
/await
intro post, which includes a brief description of howTask
awaiters useSynchronizationContext
and introduces some best practices. - The Async/Await FAQ, which goes into more detail on the contexts.
- This MSDN forum post.
- Stephen Toub demos this deadlock, and so does Lucian Wischik.
2012 年 7 月 13 日更新: 包含此答案 写入博客文章.
这篇关于调用 await GetFileAsync() 永远不会返回,并且应用程序在 WinRT 应用程序中挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!