本文介绍了如何处理跨线程访问异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在WPF中使用多个线程时,一个常见的例外是:

A common exception one can get when working with multiple threads in WPF is:

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

What are the options to deal with this properly?

推荐答案

根据情况,有多种选择:

Depending on the situation there are various options:

从另一个线程访问控件

  • Data Binding:

在这种情况下,您最容易做的就是避免与控件直接交互.您可以绑定您要访问的属性或将其修改为其类为 ,然后在该对象上设置属性.该框架将为您处理其余的工作. (通常,您很少需要直接与UI元素进行交互,您几乎总是可以绑定各自的属性并改为使用绑定源;在这种情况下,可能需要直接进行控件访问是控件创作.)

In this case the easiest thing you can do is avoiding the direct interaction with the control. You can just bind the property you want to access or modify to an object whose class implements INotifyPropertyChanged and then set the property on that object instead. The framework will handle the rest for you. (In general you rarely should need to interact with UI-elements directly, you can almost always bind the respective properties and work with the binding source instead; one case where direct control access may be necessary is control authoring.)

在某些情况下,仅数据绑定是不够的,例如,当尝试修改绑定的 ObservableCollection<T> ,为此,您需要...

There are some cases where data binding alone is not enough, for example when trying to modify a bound ObservableCollection<T>, for this you need...

调度:

Dispatching:

您可以将访问代码调度到拥有该对象的线程,这可以通过调用或 Dispatcher 上,该对象拥有已访问(可以在另一个线程上获取此Dispatcher).

You can dispatch your accessing code to the thread owning the object, this can be done by calling Invoke or BeginInvoke on the Dispatcher owning the object being accessed (getting this Dispatcher is possible on another thread).

例如

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

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

If it is not clear on which thread a method is executed you can use Dispatcher.CheckAccess to either dispatch or execute an action directly.

例如

void Update()
{
    Action action = () => myTextBlock.Text = "Test";
    var dispatcher = myTextBlock.Dispatcher;
    if (dispatcher.CheckAccess())
        action();
    else
        dispatcher.Invoke(action);
}

如果对象不是 DispatcherObject 仍然需要关联的Dispatcher,您可以使用 Dispatcher.CurrentDispatcher 在创建对象的线程中 (因此,在线程执行的方法中执行此操作不会带来任何好处).为了方便起见,通常在应用程序的主UI线程上创建对象;您可以使用Application.Current.Dispatcher从任何地方获取该线程的Dispatcher.

If an object is not a DispatcherObject and you still need the associated Dispatcher you can use Dispatcher.CurrentDispatcher in the thread creating the object (so doing this in the method being executed by a thread will not do you any good). For convenience as you usually create objects on the application's main UI thread; you can get that thread's Dispatcher from anywhere using Application.Current.Dispatcher.

特殊情况:

在发生 ProgressChanged 时将所有控件访问权限在创建实例的线程上(当然应该是UI线程)

Move any control access to ProgressChanged as it occurs on the thread that created the instance (which should of course be the UI-thread)

计时器

在WPF中,为了方便起见,可以使用 DispatcherTimer ,它将为您执行调度,因此 Tick .如果您可以将调度委派给数据绑定系统,那么您当然也可以使用普通计时器.

In WPF you can use the DispatcherTimer for convenience, it does the dispatching for you so any code in Tick is invoked on the associated dispatcher. If you can delegate the dispatching to the data binding system you of course can use a normal timer as well.

您可以阅读有关MSDN上一般上的Dispatcher队列如何工作和WPF线程的更多信息.

You can read more about how the Dispatcher queue works and WPF threading in general on MSDN.

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

如果所讨论的对象不是 Freezable ,则应一般而言,避免在另一个线程上创建它或限制对创建线程的访问.如果是Freezable,则只需致电 Freeze 使其可以被其他线程访问.

If the object in question is not Freezable you should in general simply avoid creating it on another thread or restricting access to the creating thread. If it is Freezable you just need to call Freeze to make it accessible to other threads.

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

也就是说,正在更新其实例的类型是用户代码.如果引发异常,则可能是使用 DependencyObject 的某人引起的.作为数据类的基本类型.

That is, the type whose instance is being updated is user-code. If an exception is thrown this situation probably came about by someone using DependencyObject as base type for a data class.

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

This situation is the same as accessing a control and the same approaches can be applied but usually it should be avoided in the first place. Granted, this allows for simple property change notifications via dependency properties and those properties can also be bound but often enough this is just not worth giving up thread-independency. You can get change notifications from INotifyPropertyChanged and the binding system in WPF is inherently asymmetrical, there always is a property that is bound (target) and something that is the source for this binding. Usually the UI is the target and the data is the source, meaning that only UI components should need dependency properties.

这篇关于如何处理跨线程访问异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 06:17
查看更多