在WPF中使用多个线程时可以获得的常见异常是:
调用线程无法访问此对象,因为另一个线程拥有它
有什么方法可以妥善处理这个问题?
答案 0 :(得分:37)
根据具体情况,有多种选择:
从另一个线程访问控件
e.g。使用进度信息更新TextBlock。
<强> Data Binding 强>
在这种情况下,您可以做的最简单的事情是避免与控件直接交互。您只需bind要访问的属性或修改为类implements INotifyPropertyChanged
的对象,然后在该对象上设置属性。该框架将为您处理其余部分。 (通常,您很少需要直接与UI元素交互,您几乎总是可以绑定相应的属性并使用绑定源;可能需要直接控制访问的一种情况是控件创作。)
在某些情况下单独使用数据绑定是不够的,例如在尝试修改绑定ObservableCollection<T>
时,您需要...
<强> Dispatching 强>
您可以将访问代码分派给拥有该对象的线程,这可以通过调用拥有正在访问的对象的Invoke
上的BeginInvoke
或Dispatcher
来完成(获取此{{ 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
。
特殊情况:
在创建实例的线程上发生对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);
});