来自不同线程的火灾事件
在我的主窗口( 线程A )中,我启动一个新线程( 线程B ),它在用户等待时完成一些工作。
如果出现错误或用户需要额外信息,线程B将触发事件,线程A将侦听这些事件。
在线程A的事件监听器中,我需要向用户显示对话框消息,我有一个自定义对话框窗口并使用dialogWindow.showDialog()
显示它。 这工作正常,但当我尝试设置对话框的所有者时会导致错误,我执行此对话窗口dialogWindow.Owner = Window.GetWindow(this)
。
我得到的错误是: 调用线程无法访问此对象,因为另一个线程拥有它。
侦听从不同线程触发的事件的正确方法是什么?
从后台线程向UI线程引发事件的正确方法是,应该在该Dispatcher上引发事件,这里的键是获取UIthread的调度程序。
UIDisaptcher.BeginInvoke((ThreadStart)(()=> RaiseEventToUIThread()));
当UI线程侦听raise事件时,它可以设置Owner属性(因为窗口是由UI线程创建的)。
事件侦听器代码将在触发事件的线程中隐式运行,因此事件侦听器不是线程绑定的。
如果您希望在事件处理过程中在UI中显示某些内容,则应自行进行编组。 像这样的东西:
void OnEvent(object sender, EventArgs args) { // runs in the event sender's thread string x = ComputeChanges(args); Dispatcher.BeginInvoke((Action)( () => UpdateUI(x) )); } void UpdateUI(string x) { // runs in the UI thread control.Content = x; // - or - new DialogWindow() { Content = x, Owner = this }.ShowDialog(); // - or whatever }
所以:你在后台线程中优先执行计算(如果有的话),而不触及UI; 之后,当您知道UI中需要进行哪些更改时,您将在UI线程中执行UI更新代码。
Dispatcher
是控件的属性,因此如果您的代码是UI的一部分,您将免费获得Dispatcher。 否则,您可以从任何控件(例如, control.Dispatcher
)中获取调度程序。
当然 – >我们要做的是使用SynchronizationContext。 因此,当您启动一个新线程时,您捕获(在UI线程上)当前上下文并将其作为参数传递给第二个线程。
然后在第二个线程上,当你想要提出一个事件时,你这样做:
if (_uiThreadId != Thread.CurrentThread.ManagedThreadId) { _uiContext.Post( new SendOrPostCallback(delegate(object o) { OnYourEvent((EventArgs)o); }), e); } else OnYourEvent(e);