In the below sample, when the async "Process" function is invoked synchronously, I can see the call to "await Task.Delay(1000)" causes the UI to hang.
I know I can avoid the hang by either calling "await Task.Delay(1000).ConfigureAwait(false)" or wrapping the "Process" invocation inside another task. I can understand the problem is with the synchornizationcontext, and I know await is doing something fancy with it i.e. if I replace the call "await Task.Delay(1000)" to "Task.Delay(1000).Wait()" the UI does not hang.
Can someone please explain the behavior (i did try looking at ildasm code but it didn't help). Thanks much.
public MainWindow()
{
InitializeComponent();
Loaded += OnLoaded;
}
public async void OnLoaded(object sender, RoutedEventArgs args)
{
var task = Process();
MessageBox.Show(task.Result);
}
public async Task<String> Process()
{
await Task.Delay(1000);
return "";
}
First, methods are always invoked synchronously. When OnLoaded
is invoked, it invokes the Process
method.
The Process
method invokes the Task.Delay
method and gets back a reference to a Task<string>
object which it awaits. This means that the code after the await
is executed later and that the Process
method returns at this point.
The OnLoaded
method gets back a reference to a Task<string>
object which it stores in the task
variable. Then invokes the Result
getter on the task. This blocks the current thread until the task completes.
One second later, the Process
method tries continue. Because you started the task on the UI thread, the scheduler attempts to schedule the Process
method on the UI thread. But the UI thread is blocked by the Result
getter call, so the return "";
statement is never executed.
task.Result
means "hang the UI until the task is complete, then use the result".await task
means "yield control to my caller; when the task is complete, schedule the continuation of the task to run at a convenient time in the future, and use the result then". - Eric Lippert