12

有人可以阐明我遇到的问题吗?

我正在做一个 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();
    }

感谢您的时间!

4

2 回答 2

23

因此,如果我正确理解您的问题,您是在说这段代码完全按照您想要的方式工作,但您只是想了解它是如何(以及为什么)工作的?

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

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 内部的代码),但循环的基础是相同的。

于 2011-05-09T04:17:04.993 回答
4

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

于 2011-05-03T10:13:45.783 回答