如何处理跨线程访问异常?

时间:2012-08-12 16:36:40

标签: wpf multithreading

在WPF中使用多个线程时可以获得的常见异常是:

  

调用线程无法访问此对象,因为另一个线程拥有它

有什么方法可以妥善处理这个问题?

3 个答案:

答案 0 :(得分:37)

根据具体情况,有多种选择:

从另一个线程访问控件

e.g。使用进度信息更新TextBlock。

  • <强> Data Binding

    在这种情况下,您可以做的最简单的事情是避免与控件直接交互。您只需bind要访问的属性或修改为类implements INotifyPropertyChanged的对象,然后在该对象上设置属性。该框架将为您处理其余部分。 (通常,您很少需要直接与UI元素交互,您几乎总是可以绑定相应的属性并使用绑定源;可能需要直接控制访问的一种情况是控件创作。)

    在某些情况下单独使用数据绑定是不够的,例如在尝试修改绑定ObservableCollection<T>时,您需要...

  • <强> Dispatching

    您可以将访问代码分派给拥有该对象的线程,这可以通过调用拥有正在访问的对象的Invoke上的BeginInvokeDispatcher来完成(获取此{{ 1}}可能在另一个线程上。)

    e.g。

    Dispatcher
    new Thread(ThisThreadStart).Start();
    

    如果不清楚方法执行的是哪个线程,您可以使用Dispatcher.CheckAccess直接调度或执行操作。

    e.g。

    void ThisThreadStart()
    {
        textBlock.Dispatcher.Invoke(new Action(() => textBlock.Text = "Test"));
    }
    

    如果某个对象不是DispatcherObject且您仍然需要关联的void Update() { Action action = () => myTextBlock.Text = "Test"; var dispatcher = myTextBlock.Dispatcher; if (dispatcher.CheckAccess()) action(); else dispatcher.Invoke(action); } ,则可以在创建对象的主题中使用Dispatcher.CurrentDispatcher (这样做在由线程执行的方法中对你没有任何好处)。为方便起见,您通常会在应用程序的主UI线程上创建对象;您可以使用Dispatcher从任何地方获取该帖子的Dispatcher

特殊情况:

  • BackgroundWorker

    在创建实例的线程上发生对ProgressChanged的任何控制访问(当然应该是UI线程)

  • 计时器

    在WPF中,您可以使用DispatcherTimer来方便,它会为您执行调度,因此在关联的调度程序上调用Tick中的任何代码。如果您可以将调度委托给数据绑定系统,您当然也可以使用普通计时器。

您可以详细了解Application.Current.Dispatcher队列如何工作以及WPF线程一般on MSDN

访问在另一个帖子上创建的对象

e.g。在后台加载图像。

如果有问题的对象不是Freezable,通常只需避免在另一个线程上创建它或限制对创建线程的访问。如果是Dispatcher,您只需要调用Freeze即可让其他线程访问它。

从其他线程访问数据对象

即,正在更新其实例的类型是用户代码。如果抛出异常,则可能是某人使用DependencyObject作为数据类的基本类型。

这种情况与访问控件相同,并且可以应用相同的方法,但通常应首先避免使用。当然,这允许通过dependency properties进行简单的属性更改通知,并且这些属性也可以绑定,但通常不足以放弃线程独立性。您可以从INotifyPropertyChanged获取更改通知,并且WPF中的绑定系统本质上是不对称的,始终存在绑定(目标)的属性以及此绑定的源。通常,UI是目标,数据是源,这意味着只有UI组件应该需要依赖属性。

答案 1 :(得分:0)

这将是几百行代码,对于我“弄明白”的东西。

但摘要是:

App_OnStartup 生成后台线程

在回调中,

呼叫

Application.Current.MainWindow.Dispatcher.CheckAccess() - 获取异常 Application.Current.Dispatcher.CheckAccess()不是

答案 2 :(得分:0)

我有一个udp监听器对象,通过我的mainWindow wpf .cs文件中的方法/回调+ =&#的事件进行通信。

使用参数调用事件处理函数,其中一个是我想要在mainWindow.cs的列表框中显示的消息

H.B.使用此主题中的信息。以上; 我使用以下代码在我的eventhandler回调中添加,测试和处理了wpf中的crossthread,但我使用的是真正的消息而不是硬编码的消息:

listBox1.Dispatcher.Invoke(new Action(() => listBox1.Items.Add("MessageHere")));

更新:

这样做更好,因为你可以在匿名函数中添加更多东西。

 listBox1.Dispatcher.Invoke((Action)delegate 
 {
     listBox1.Items.Add(e.ReaderMessage); 
 });