被Dispatcher.BeginInvoke()的行为困惑

xia*_*jie 10 wpf dispatcher begininvoke

有人可以解决我遇到的问题吗?

我正在研究一个wpf项目.方案如下:

我需要在主UI线程上弹出一个窗口(模型窗口),然后关闭它.这些工作是从另一个UI线程开始的(阻止用户点击主UI窗口.)然后我关闭这个窗口.主要代码如下所示.它有效.

据我所知,close方法在ShowDialog()返回之前不会被执行(至少在UI线程上是这种情况,我的意思是没有调度程序的代码),有没有人有多线程的经验?

   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();
    }
Run Code Online (Sandbox Code Playgroud)

感谢您的时间!

Joe*_*ite 19

所以,如果我正确地理解你的问题,你说,这个代码的工作正是你想要的方式,但你只是想了解如何(以及为什么它)的作品?

这是它的工作原理.首先,您的线程运行此代码:

Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
    window = new Window();
    window.ShowDialog();
}));
Run Code Online (Sandbox Code Playgroud)

将您的操作排入主(UI)线程的调度程序队列,然后立即返回:您的工作线程继续运行.

当应用程序首次启动时(通常通过编译器生成的代码初始化您的App.xaml对象,虽然您也可以通过调用Application.Run显式执行),但它启动了它的消息循环,类似于这样(伪代码,非常非常简化):

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

因此,在排队操作后不久的某个时刻,UI线程将绕过将您的操作从队列中拉出并运行它,此时您的操作会创建一个窗口并以模态方式显示它.

模态窗口现在启动它自己的消息循环,这就像这样(再次,非常简化):

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

稍后,您的工作线程运行此代码:

Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
    window.Close();
}));
Run Code Online (Sandbox Code Playgroud)

同样,您的操作排队到UI线程的调度程序队列,然后BeginInvoke调用立即返回,您的工作线程继续运行.

因此,UI线程的消息循环迟早会出现并执行您的操作,这会告诉窗口关闭.这与用户单击标题栏的"X"按钮的效果基本相同,即使您在模态对话框中,这当然也可以完成.这会导致ShowDialog的消息循环终止(因为窗口现在已关闭),此时对话框被隐藏,其他窗口重新启用,ShowDialog返回,原始(ShowDialog)操作完成,因此返回,控制落下回到Application.Run中的原始消息循环.

请注意,每个线程有一个调度程序队列,而不是每个消息循环一个.因此,您的"关闭"操作会进入与"显示对话框"操作相同的队列.这是一个不同的代码片段现在正在进行消息循环轮询(ShowDialog中的一个而不是Application.Run中的一个),但循环的基础是相同的.