如何处理跨线程访问exception?
在WPF中使用multithreading时可以得到一个常见的exception:
调用线程不能访问这个对象,因为不同的线程拥有它
有什么方法可以正确处理这个问题?
根据情况有不同的select:
从另一个线程访问一个控件
例如用进度信息更新一个TextBlock。
-
数据绑定 :
在这种情况下,您可以做的最简单的事情就是避免与控件的直接交互。 您可以将要访问或修改的属性绑定到类实现
INotifyPropertyChanged
的对象,然后设置该对象的属性。 框架将为您处理剩下的事情。 (一般来说,你很less需要直接与UI元素进行交互,你几乎总是可以绑定相应的属性,并使用绑定源代替;直接控制访问可能是必要的控制创作。在某些情况下,数据绑定本身是不够的,例如,当试图修改绑定的
ObservableCollection<T>
,为此你需要… -
调度 :
您可以将您的访问代码分派给拥有该对象的线程,这可以通过在拥有被访问对象的
Dispatcher
上调用Invoke
或BeginInvoke
(在另一个线程上获得该Dispatcher
器)来完成。例如
new Thread(ThisThreadStart).Start();
void ThisThreadStart() { textBlock.Dispatcher.Invoke(new Action(() => textBlock.Text = "Test")); }
如果不清楚在哪个线程上执行某个方法,则可以使用
Dispatcher.CheckAccess
来直接调度或执行一个操作。例如
void Update() { Action action = () => myTextBlock.Text = "Test"; var dispatcher = myTextBlock.Dispatcher; if (dispatcher.CheckAccess()) action(); else dispatcher.Invoke(action); }
如果一个对象不是一个
DispatcherObject
并且你仍然需要关联的Dispatcher
你可以在创build该对象的线程中使用Dispatcher.CurrentDispatcher
(所以在线程执行的方法中这样做对你没有任何好处)。 为了方便起见,通常在应用程序的主UI线程上创build对象; 您可以使用Application.Current.Dispatcher
从任何地方获取该线程的Dispatcher
。
特殊情况:
-
BackgroundWorker
将任何对
ProgressChanged
控制权移交给创build实例的线程(当然这应该是UI线程) -
计时器
在WPF中,为方便起见,您可以使用
DispatcherTimer
,它会为您调度,以便在关联的调度程序上调用Tick
任何代码。 如果您可以将调度委托给数据绑定系统,那么您当然也可以使用普通定时器。
您可以在MSDN上了解更多关于Dispatcher
队列的工作原理和WPF线程。
访问另一个线程上创build的对象
例如在后台加载图像。
如果所讨论的对象不是Freezable
那么通常应该避免在另一个线程上创build它,或者限制对创build线程的访问 。 如果是Freezable
,则只需调用Freeze
即可让其他线程访问它。
从另一个线程访问数据对象
也就是说,其实例正在更新的types是用户代码。 如果抛出一个exception,这种情况可能是由某人使用DependencyObject
作为数据类的基本types。
这种情况与访问控件一样,可以应用相同的方法,但通常应该首先避免。 当然,这允许通过依赖项属性进行简单的属性更改通知,这些属性也可以被绑定,但通常这还不足以放弃线程独立性。 您可以从INotifyPropertyChanged
获取更改通知,并且WPF中的绑定系统本质上是不对称的,始终存在绑定的属性(目标),以及此绑定的来源。 通常UI是目标,数据是源,意味着只有UI组件应该需要依赖属性。
这将是几百行代码,对于我“想通”的东西。
但是总结是:
App_OnStartup生成一个后台线程
在callback中,
呼叫
Application.Current.MainWindow.Dispatcher.CheckAccess() – 获取exceptionApplication.Current.Dispatcher.CheckAccess()不
我有一个udp监听器对象,通过在我的mainWindow wpf .cs文件中的方法/callback+ ='编辑的事件进行通信。
使用参数调用事件处理函数,其中一个是我想要在mainWindow.cs的列表框中显示的消息
使用上面的HB的这个线程中的信息; 我已经使用下面的代码添加,testing并处理了我的eventhandlercallback中的wpf中的crossthread,但是我使用了一个不是硬编码的真实消息:
listBox1.Dispatcher.Invoke(new Action(() => listBox1.Items.Add("MessageHere")));
更新:
这是更好的,因为你可以把更多的东西放在匿名函数中。
listBox1.Dispatcher.Invoke((Action)delegate { listBox1.Items.Add(e.ReaderMessage); });