Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does WPF's Dispatcher.Invoke not cause a deadlock when run on the main thread?

Consider the code:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            //System.Threading.Thread.CurrentThread = button.Dispatcher.Thread
            button.Dispatcher.Invoke(() => button.Content = "1234");
        }
    }

Of course, button_Click is run on the main thread.

My understanding is that button.Dispatcher.Thread is the main thread and the Invoke() will get processed only when the thread is not blocked. However, isn't the main thread blocked in this case? I.e. the main thread is waiting for the Dispatcher.Invoke() call to complete and Dispatcher.Invoke() is waiting for the main thread to free up. So I expect a deadlock here, but it doesn't get deadlocked.

Why?

P.S: I am aware that in this situation I don't need the Dispatcher.Invoke and that I can call the button.Content = "1234" directly. I am trying to understand why the deadlock DOES NOT happen in this case.

like image 315
Frank Adams Avatar asked Jan 04 '23 04:01

Frank Adams


1 Answers

I believe your misunderstanding may be based around the following thought process:

"Well, Invoke blocks the calling thread until the action is completed. How can it perform the action on the thread if the thread is blocked?"

If we look inside the source, we see that the callback is being called not only on the same thread, but directly* inside the Invoke method. The main thread is not being blocked.

If you look at the dispatcher's Reference Source page, you can see the following comment above an if statement within the Invoke method's implementation, with the callback being called within it:

// Fast-Path: if on the same thread, and invoking at Send priority,
// and the cancellation token is not already canceled, then just
// call the callback directly.
if(!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess())
{
    /* snipped */

    callback();

    /* snipped */
}

You're calling Dispatcher.Invoke on the main thread, and the method handles that by just calling it instantly.

*Well, not directly, but the entire body of Invoke(Action) is just a call to the method that the above code is in.

like image 99
Vassalware Avatar answered Jan 06 '23 17:01

Vassalware