4

I tried a simple async-await example with and without .ConfigureAwait(false).
With .ConfigureAwait(false) you can update the ui via the dispatcher, which is unneccesary without it.
This is case 1 and 3 in the code below - that works and I can understand how it works.

My question is about case 2 where I add a - completely unneccesary -- refresh
Action(() => { }) via the dispatcher.
This occasionally freezes my ui. Especially after invoking the eventhandler repeatedly.
Can anybody explain why the ui freezes in case 2?

private void Test_Click(object sender, RoutedEventArgs e)
{
    Test();
}

public async void Test()
{
    Print("Start task");

    // Case 1 & 2
    await Task.Delay(2000);
    // Case 3
    await Task.Delay(2000).ConfigureAwait(false);

    Print("Finished task");
}

void Print(string text)
{
    // Case 1 & 2
    Output.Inlines.Add(new Run(text) { Foreground = Brushes.Blue, FontWeight = FontWeights.Bold });
    // Case 2 only
    Output.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(() => { }));

    // Case 3
    Output.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, 
       new Action(() => 
       {  
         Output.Inlines.Add(new Run(text) { Foreground = Brushes.Blue, FontWeight = FontWeights.Bold 
       }); }));
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Gerard
  • 13,023
  • 14
  • 72
  • 125
  • Pause in the debugger and look at the stack trace. – SLaks Sep 28 '14 at 14:04
  • 3
    It's not the `Action(() => {})` that is pausing your UI. It's the `Task.Delay(2000)` that does not have the `ConfigureAwait(false)`. Since it is configured to run on the UI thread, your UI thread will hang for 2 seconds at an unspecified time. That unspecified time turns out being the run of case 2. – Raymond Chen Sep 28 '14 at 14:13
  • @RaymondChen: the delay is awaited and doesn't freeze at all. Only after adding the empty action it does. – Gerard Sep 28 '14 at 14:35
  • @SLaks When it is frozen I cannot pause the debugger. The call stack after returning from await starts with: resuming async action. – Gerard Sep 28 '14 at 14:37
  • Huh? You should **always** be able to pause the debugger, unless Visual Studio itself is broken. You may need to disable Just My Code to see the stack trace. – SLaks Sep 28 '14 at 14:38
  • 1
    @RaymondChen: No; that's still an async call; it's just resuming on the UI thread. – SLaks Sep 28 '14 at 14:40
  • `Dispatcher.Invoke` is a synchronous method. It will wait until the UI message queue is available to process your action. If that queue is busy processing other messages, your UI will freeze. – Yuval Itzchakov Sep 28 '14 at 14:40
  • Do you run these "cases" separately each time? or do you invoke them all every click? – Yuval Itzchakov Sep 28 '14 at 14:42
  • @YuvalItzchakov I just run case 2. – Gerard Sep 28 '14 at 14:46
  • @SLaks: well in fact my whole laptop freezes and only via starting the task manager and closing it, visual studio becomes reactive again. That strangeness is the reason I ask. – Gerard Sep 28 '14 at 14:48
  • Is any other piece of code executing in parallel to this? – Yuval Itzchakov Sep 28 '14 at 14:49
  • How many cores do you have? Can you lower the priority of your app process in Task Manager so you can hit pause in VS? If you can use Task Manager, why can't you use VS too? – SLaks Sep 28 '14 at 14:50
  • Also, what version of .Net? This may be a bug in the dispatcher. – SLaks Sep 28 '14 at 14:58
  • Does this occur when running the process outside of `vshost`? – Yuval Itzchakov Sep 28 '14 at 14:59
  • @YuvalItzchakov: Yes; I reproduced it. It's hanging on `GetMessageW()`, which is weird. This looks like a bug in WPF. – SLaks Sep 28 '14 at 15:03
  • Yes i see that happen now. – Yuval Itzchakov Sep 28 '14 at 15:05
  • Thanks for trying and reproducing. Relieved it isn't me. – Gerard Sep 28 '14 at 15:19
  • @RaymondChen: My previous comment is totally wrong. Any idea why `GetMessage(msg, 0, 0, 0)` would hang? – SLaks Sep 28 '14 at 15:26
  • Similar issue here: http://stackoverflow.com/questions/4127625/why-would-getmessagew-take-up-massive-cpu-usage-in-my-wpf-application – Yuval Itzchakov Sep 28 '14 at 16:24
  • @Gerard: While this is interesting, for real-world code you should be using `IProgress` rather than `Dispatcher`. – Stephen Cleary Sep 28 '14 at 18:26

1 Answers1

3

This is a bug in WPF.

It runs a filtered GetMessage() loop (see here), which will hang if the is a message stuck in the queue that doesn't meet the filter.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964