Reputation: 18795
I am porting a Windows Phone App to Windows 8 and I have problems trouble with async/await. There is a class that needs a while to load all its data. To not block the UI thread the loading is done in a separate task/thread. Once the loading is complete the UI thread needs to be updated:
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...
});
}
}
On Windows Phone the Output is something like this:
StartLoad: 1
Reading: 4
EndLoad: 1
When I execute the same code in my Windows Store app the output is:
StartLoad: 1
Reading: 4
EndLoad: 4
While on Windows Phone the ThreadId is the same befor and after the await on Windows 8 the ThreadId after the await is NOT the same as before the await. Here it is the same Thread as in the background task. Because of the this UpdateUI() is executed in the wrong (non UI) thread and the app crashes...
What is the reason for this different behaviour?
. .
EDIT: Well, this is interesting: Although in both cases the process is started from the same thread (3), the problem only shows up when the process is startet from the App() constructor but not when started from OnLaunched. Any idea why this is the case?
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();
}
}
This creates:
App: 3
StartLoad: 3
Reading: 4
EndLoad: 4
OnLaunched: 3
StartLoad: 3
Reading: 5
EndLoad: 3
Upvotes: 0
Views: 803
Reputation: 203812
You were running the code from the thread that would eventually start the applications message loop, but it hadn't done so at that point in time. The SynchronizationContext
that represents the UI hadn't yet been created and set as the current context. As such, the async
method didn't have a context to capture and send the continuations to after each await
; the default context was used instead, sending the continuations to the thread pool.
You need to run the code later on, once the SynchronizationContext
has already been set up. This is why it works when you call it from OnLaunched
; that runs after the context is set up.
If you look at the value of SynchronizationContext.Current
, you'll see that it's null in your first case, and has a value in the second. This, rather than checking the current thread's ID, is a much better indicator of whether or not continuations will be able to run in the UI thread.
Upvotes: 5
Reputation: 457302
The most likely cause for this is that LoadData
is not actually being called on the UI thread.
Upvotes: 0