如何处理跨线程访问exception?

在WPF中使用multithreading时可以得到一个常见的exception:

调用线程不能访问这个对象,因为不同的线程拥有它

有什么方法可以正确处理这个问题?

根据情况有不同的select:

从另一个线程访问一个控件

例如用进度信息更新一个TextBlock。

  • 数据绑定 :

    在这种情况下,您可以做的最简单的事情就是避免与控件的直接交互。 您可以将要访问或修改的属性绑定到类实现 INotifyPropertyChanged的对象,然后设置该对象的属性。 框架将为您处理剩下的事情。 (一般来说,你很less需要直接与UI元素进行交互,你几乎总是可以绑定相应的属性,并使用绑定源代替;直接控制访问可能是必要的控制创作。

    在某些情况下,数据绑定本身是不够的,例如,当试图修改绑定的ObservableCollection<T> ,为此你需要…

  • 调度 :

    您可以将您的访问代码分派给拥有该对象的线程,这可以通过在拥有被访问对象的Dispatcher上调用InvokeBeginInvoke (在另一个线程上获得该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); });