Confused by the behavior of Dispatcher.BeginInvoke ()

Can anyone shed some light on the problem that I have?

I am working on a wpf project. The scenario is as follows:

I need to open a window (model window) in the main user interface thread, and then close it. These works are started from another user interface thread (so that the user does not click on the main user interface window). Then I close this window. The main code is shown below. And it works.

As far as I know, the close method will not be used until ShowDialog() returns ShowDialog() at least this refers to the user interface thread, I mean the code without the dispatcher), does anyone have any experience with multi-threaded?

  Window window; private void Button_Click(object sender, RoutedEventArgs e) { Thread thread = new Thread(() => { //create a window and let user work from this thread //code is omitted. //create another window on main UI thread Application.Current.Dispatcher.BeginInvoke(new Action(() => { window = new Window(); window.ShowDialog(); })); //do some work here Thread.Sleep(1000); Application.Current.Dispatcher.BeginInvoke(new Action(() => { //Thread.Sleep(1000); window.Close(); })); }); thread.Start(); } 

Thank you for your time!

+7
source share
2 answers

So, if I understood your question correctly, you say that this code works exactly the way you want, but you are just trying to understand how (and why) it works?

Here's how it works. Firstly, your thread executes this code:

 Application.Current.Dispatcher.BeginInvoke(new Action(() => { window = new Window(); window.ShowDialog(); })); 

This is the sequence of your actions in the main dispatch queue (UI), and then immediately returns: the workflow continues to work.

When you first launch the application (usually using code generated by the compiler that initializes your App.xaml object, although you can also do this explicitly by calling Application.Run), it starts its message loop, which looks something like this: (pseudocode very very simplified):

 public class Application { public void Run() { while (!Exited && action = Dispatcher.DequeueAction()) action(); } } 

So, at some point, soon after you put the queue into action, the user interface thread will bypass to pull your action out of the queue and run it, after which your action will create a window and show it modally.

Now the modal window starts its own message loop, which looks something like this (again, very simplified):

 public class Window { public bool? ShowDialog() { DisableOtherWindowsAndShow(); while (!IsClosed && action = Dispatcher.DequeueAction()) action(); EnableOtherWindowsAndHide(); return DialogResult; } } 

Later, your worker thread runs this code:

 Application.Current.Dispatcher.BeginInvoke(new Action(() => { window.Close(); })); 

Again, your action is queued up on the UI thread manager queue, and then the BeginInvoke call returns immediately and the workflow continues to work.

So sooner or later the message loop of the user interface stream will get by and begin to perform your action, which indicates the closing of the window. This has the same effect as the user clicking the β€œX” button in the title bar, which, of course, works great even if you are in a modal dialog box. This leads to the end of the ShowDialog message loop (because the window is now closed), after which the dialog is hidden and the other windows are turned on again, ShowDialog returns, the original action (ShowDialog) is completed and therefore returns, and control drops back to the original message outline in the Application .Run.

Note that in each thread there is one dispatch queue, and not one message line. Thus, your "closed" action goes in the same queue as the "show dialogue" action. This is another piece of code that now polls as a message loop (one inside ShowDialog instead of one inside Application.Run), but the basics are the same.

+14
source

BeginInvoke - non-blocking method; it adds an action to the dispatcher queue and does not wait for its completion. Instead, you should use Invoke , which calls the method synchronously in the dispatcher thread.

+3
source

All Articles