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

Wpf 如何处理跨线程访问异常?,wpf,multithreading,Wpf,Multithreading,在WPF中处理多个线程时可能遇到的常见异常是: 调用线程无法访问此对象,因为其他线程拥有它 正确处理此问题的选项有哪些?根据具体情况,有多种选项: 从另一个线程访问控件 e、 g.使用进度信息更新文本块 : 在这种情况下,您可以做的最简单的事情就是避免与控件的直接交互。您可以仅将要访问或修改的属性设置为其类的对象,然后在该对象上设置该属性。框架将为您处理其余部分。(通常,您很少需要直接与UI元素交互,您几乎总是可以绑定各自的属性并使用绑定源;需要直接控制访问的一种情况是控制编写。) 在某些情况

在WPF中处理多个线程时可能遇到的常见异常是:

调用线程无法访问此对象,因为其他线程拥有它


正确处理此问题的选项有哪些?

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

从另一个线程访问控件

e、 g.使用进度信息更新文本块

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

    在某些情况下,仅数据绑定是不够的,例如,在尝试修改绑定时,为此您需要

  • 您可以将访问代码分派给拥有该对象的线程,这可以通过调用或在拥有被访问对象的线程上完成(在另一个线程上可以获得此
    Dispatcher

    e、 g

    如果不清楚在哪个线程上执行方法,您可以使用它直接分派或执行操作

    e、 g


    如果对象不是,并且您仍然需要关联的
    Dispatcher
    ,您可以在创建对象的线程中使用它(因此在线程执行的方法中这样做对您没有任何好处)。为方便起见,通常在应用程序的主UI线程上创建对象;您可以使用
    Application.Current.Dispatcher
    从任何地方获取该线程的
    Dispatcher

特殊情况:

  • 在创建实例(当然应该是UI线程)的线程上发生任何控件访问时,将其移动到

  • 计时器

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

您可以阅读有关
调度程序
队列工作原理和WPF线程的更多信息

访问在另一个线程上创建的对象

e、 g.在背景中加载图像

如果所讨论的对象不是,通常应该避免在另一个线程上创建它或限制对创建线程的访问。如果它是
Freezable
,您只需要调用它以使其他线程可以访问它

从另一个线程访问数据对象

也就是说,正在更新其实例的类型是用户代码。如果抛出异常,这种情况可能是由某人使用作为数据类的基类型引起的


这种情况与访问控件相同,也可以采用相同的方法,但通常应首先避免。当然,这允许通过发送简单的属性更改通知,并且这些属性也可以绑定,但通常情况下,放弃线程独立性并不值得。您可以从中获得更改通知,并且WPF中的绑定系统本质上是不对称的,总是有一个绑定的属性(target)和该绑定的源。通常,UI是目标,数据是源,这意味着只有UI组件需要依赖属性。

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

但总结如下:

应用程序启动 生成一个背景线程

在回调中

召唤

Application.Current.MainWindow.Dispatcher.CheckAccess()-获取异常
Application.Current.Dispatcher.CheckAccess()没有

我有一个udp侦听器对象,该对象通过事件进行通信,其中方法/回调在我的主窗口wpf.cs文件中是+='ed

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

使用上述H.B.在此线程中的信息; 我使用以下代码在我的eventhandler回调中添加、测试和处理wpf中的交叉线程,但我使用的是真实的消息,而不是硬编码的消息:

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

这更好,因为可以在匿名函数中放入更多内容

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

所以这里有一个愚蠢的问题。当Dispatcher.CheckAccess()抛出“调用线程无法访问此对象,因为另一个线程拥有它”时,您会怎么做?@RogerWillcocks:我从未遇到过这种情况。给出一个完整的例子来重现这个…Application.Current.Dispatcher.Invoke(新操作(()=>textBox.Text=“Test”);工作得很好!非常感谢。问题在于步骤
Current.MainWindow
,而不是
MainWindow.Dispatcher
,所以是的,这不是我的建议……正如我所想的,但是如果MainWindow和Dispatcher不为null,您可能会期望一个用于告诉您跨线程访问是否安全的方法不会引发跨线程异常
void Update()
{
    Action action = () => myTextBlock.Text = "Test";
    var dispatcher = myTextBlock.Dispatcher;
    if (dispatcher.CheckAccess())
        action();
    else
        dispatcher.Invoke(action);
}
listBox1.Dispatcher.Invoke(new Action(() => listBox1.Items.Add("MessageHere")));
 listBox1.Dispatcher.Invoke((Action)delegate 
 {
     listBox1.Items.Add(e.ReaderMessage); 
 });