好的,所以我在周末发现了一些奇怪的东西。我有一个WPF应用程序,它会产生一些线程来执行后台工作。那些后台线程然后将工作项发布到我的同步上下文。除一例外,这一切都正常。当我的线程完成时,他们会将一个动作发布到调度程序上,这将打开一个弹出窗口。最终发生的事情是,如果两个线程都在Dispatcher上发布一个动作它开始处理一个,然后如果我打开一个带有Window.ShowDialog()的弹出窗口;当前执行路径暂停,等待来自对话框的反馈。但问题出现了,当对话框打开Dispatcher然后开始并立即开始运行已发布的第二个操作。这导致执行两个代码路径。第一个消息框保持打开状态,而第二个消息框正在运行,因为我的应用程序状态未知,因为第一个操作从未完成。
我发布了一些示例代码来演示我正在谈论的行为。应该发生的是,如果我发布2个动作并且第1个动作打开一个对话框,则第二个动作不应该在第一个动作完成之后运行。
public partial class Window1 : Window {
private SynchronizationContext syncContext;
public Window1() {
InitializeComponent();
syncContext = SynchronizationContext.Current;
}
private void Button_ClickWithout(object sender, RoutedEventArgs e) {
// Post an action on the thread pool with the syncContext
ThreadPool.QueueUserWorkItem(BackgroundCallback, syncContext);
}
private void BackgroundCallback(object data) {
var currentContext = data as SynchronizationContext;
System.Console.WriteLine("{1}: Thread {0} started", Thread.CurrentThread.ManagedThreadId, currentContext);
// Simulate work being done
Thread.Sleep(3000);
currentContext.Post(UICallback, currentContext);
System.Console.WriteLine("{1}: Thread {0} finished", Thread.CurrentThread.ManagedThreadId, currentContext);
}
private void UICallback(object data) {
System.Console.WriteLine("{1}: UI Callback started on thread {0}", Thread.CurrentThread.ManagedThreadId, data);
var popup = new Popup();
var result = popup.ShowDialog();
System.Console.WriteLine("{1}: UI Callback finished on thread {0}", Thread.CurrentThread.ManagedThreadId, data);
}
}
XAML只是一个带有按钮的窗口,该按钮调用Button_ClickWithout OnClick。如果按两次按钮并等待3秒钟,您将看到有两个对话框突然出现在另一个对话框中,其中预期的行为将是第一个弹出,然后一旦关闭,第二个将弹出。
所以我的问题是:这是一个错误吗?或者我该如何缓解这个问题,这样当第一个动作停止执行Window.ShowDialog()时,我只能处理一个动作?
谢谢, 劳尔
答案 0 :(得分:0)
模态对话框不会阻止所有者窗口处理消息,否则当模态对话框在其表面上移动时,您会看到它无法重绘(仅作为示例)。
为了达到你想要的效果,你必须在UI线程上实现自己的队列,可能需要一些同步来在第一个工作项到达时“唤醒它”。
编辑:
此外,如果在第二个模态对话框启动时检查UI线程的调用堆栈,您可能会发现它在堆栈中有第一个ShowDialog调用。
编辑#2:
可能有一种更简单的方法,无需实现自己的队列。如果您使用SynchronizationContext
对象代替Dispatcher
,则可以使用BeginInvoke
优先级调用DispatcherPriority.Normal
,并且它将正确排队(检查)。
答案 1 :(得分:0)
我知道这是一个老问题,但是我在等待我的问题答案(Advice on using the Dispatcher Priority and Binding)我认为这会付费转发™。
您遇到的问题是Dispatcher上的 Nested Pumping 。我建议阅读WPF Threading Model上的MSDN文章,尤其是标题为“技术细节和绊脚点”的部分,该部分是页面的三分之二。为方便起见,下面复制了描述嵌套泵送的子部分。
嵌套抽水
有时完全锁定UI线程是不可行的。 让我们考虑一下MessageBox类的Show方法。显示没有 返回,直到用户单击“确定”按钮。然而,它确实创造了一个 必须具有消息循环以便交互的窗口。而 我们正在等待用户单击确定,原始应用程序 窗口不响应用户输入。但是,它仍然继续 处理油漆消息。原始窗口何时重绘 覆盖和透露。
某些线程必须负责消息框窗口。 WPF可以 只为消息框窗口创建一个新线程,但是这个线程 将无法在原始窗口中绘制禁用的元素 (记住之前关于互斥的讨论)。相反,WPF 使用嵌套的消息处理系统。 Dispatcher类包括 一种名为PushFrame的特殊方法,用于存储应用程序 然后,当前执行点开始一个新的消息循环。当。。。的时候 嵌套的消息循环完成后,执行将在原始之后恢复 PushFrame调用。
在这种情况下,PushFrame会在调用时维护程序上下文 MessageBox.Show,它启动一个新的消息循环来重绘 背景窗口和句柄输入到消息框窗口。当。。。的时候 用户单击确定并清除弹出窗口,嵌套循环退出和 控制在Show。
之后恢复