12

誰かが私が抱えている問題に光を当てることができますか?

私はwpfプロジェクトに取り組んでいます。シナリオは次のとおりです。

メイン UI スレッドでウィンドウ (モデル ウィンドウ) をポップアップしてから閉じる必要があります。これらの作業は別の UI スレッドから開始され (ユーザーがメインの UI ウィンドウをクリックするのを防ぐため)、このウィンドウを閉じます。主なコードを以下に示します。そして、それは機能します。

私が知る限り、閉じるメソッドが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 の元のメッセージ ループに戻ります。

メッセージ ループごとに 1 つではなく、スレッドごとに 1 つのディスパッチャー キューがあることに注意してください。したがって、「閉じる」アクションは、「ダイアログを表示」アクションと同じキューに入ります。これは、メッセージ ループのポーリングを実行する別のコード (Application.Run 内のコードではなく、ShowDialog 内のコード) ですが、ループの基本は同じです。

于 2011-05-09T04:17:04.993 に答える
4

BeginInvokeノンブロッキングメソッドです。アクションをディスパッチャ キューに追加し、その完了を待ちません。Invoke代わりに、ディスパッチャ スレッドでメソッドを同期的に呼び出すを使用する必要があります。

于 2011-05-03T10:13:45.783 に答える