我正在将Windows Phone应用程序移植到Windows 8,我遇到了async / await问题。有一个类需要一段时间才能加载其所有数据。为了不阻止UI线程,加载在单独的任务/线程中完成。加载完成后,需要更新UI线程:
public class MyClass {
public MyClass() {
...
LoadData();
...
}
private async void LoadData() {
Debug.WriteLine("StartLoad: " + Environment.CurrentManagedThreadId);
await ReadDataAsync();
Debug.WriteLine("EndLoad: " + Environment.CurrentManagedThreadId);
UpdateUI();
}
private Task ReadDataAsync() {
return Task.Run(() => {
Debug.WriteLine("Reading: " + Environment.CurrentManagedThreadId);
...Read...
});
}
}
在Windows Phone上,输出如下:
StartLoad: 1
阅读:4
EndLoad: 1
当我在Windows应用商店应用中执行相同的代码时,输出为:
StartLoad:1
阅读: 4
EndLoad: 4
在Windows Phone上,ThreadId与之相同,在等待Windows 8之后,await之后的ThreadId与await之前的不同。这是与后台任务中相同的线程。因为这个UpdateUI()在错误的(非UI)线程中执行,并且应用程序崩溃...
这种不同行为的原因是什么?
。
修改 好吧,这很有意思:尽管在这两种情况下,进程都是从同一个线程(3)开始的,但问题只会在进程从App()构造函数启动时显示,而不是从OnLaunched启动时显示。知道为什么会这样吗?
sealed partial class App : Application {
public App() {
this.InitializeComponent();
this.Suspending += OnSuspending;
Debug.WriteLine("App: " + Environment.CurrentManagedThreadId);
MyClass test = new MyClass();
}
protected override async void OnLaunched(LaunchActivatedEventArgs e) {
Debug.WriteLine("OnLaunched: " + Environment.CurrentManagedThreadId);
MyClass test = new MyClass();
}
}
这会创建:
App:3
StartLoad:3
阅读:4
EndLoad: 4
OnLaunched:3
StartLoad:3
阅读:5
EndLoad: 3
答案 0 :(得分:5)
您正在运行最终启动应用程序消息循环的线程中的代码,但此时尚未执行此操作。表示UI的SynchronizationContext
尚未创建并设置为当前上下文。因此,async
方法没有上下文来捕获并在每个await
之后发送延续;而是使用了默认上下文,将延续发送到线程池。
一旦SynchronizationContext
已经设置,您需要稍后运行代码。这就是当你从OnLaunched
打电话时它起作用的原因;在设置上下文后运行。
如果你看一下SynchronizationContext.Current
的值,你会发现它在你的第一种情况下是空的,并且在第二种情况下有一个值。这不是检查当前线程的ID,而是更好地指示continuation是否能够在UI线程中运行。
答案 1 :(得分:0)
最可能的原因是实际上没有在UI线程上调用LoadData
。