被Dispatcher.BeginInvoke()的行为困惑

时间:2011-05-03 09:41:26

标签: wpf dispatcher begininvoke

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

我正在开发一个wpf项目。方案如下:

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

据我所知,在ShowDialog()返回之前不会出现close方法(至少在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();
    }

感谢您的时间!

2 个答案:

答案 0 :(得分:19)

因此,如果我正确理解您的问题,您就会说这段代码完全符合您的要求,但您只是想了解如何(和为什么)它有效吗?

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

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

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

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

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

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

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

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

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

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

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

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

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

答案 1 :(得分:3)

BeginInvoke是一种非阻塞方法;它将操作添加到调度程序队列,而不是等待其完成。您应该使用Invoke代替,它在调度程序线程上同步调用该方法。