调用线程无法访问对象,因为不同的线程拥有它

时间:2011-07-02 04:57:24

标签: wpf multithreading

在我写这篇文章之前,我以为我知道导致这种异常的原因是什么:

var menu = ViewConfigHelper.CreateObjectFromResource<Menu>(config, baseURI);
if (!menu.Dispatcher.CheckAccess())
{
    throw new ArgumentException("Somethign wrong");
}
if (!LayoutRoot.Dispatcher.CheckAccess())
{
    throw new ArgumentException("SOmethign wrong");
}
 // exception throw here
LayoutRoot.Children.Insert(0, menu);

第一行从嵌入的XAML文件创建一个Menu控件。两个CheckAccess调用都返回true。但是,当执行最后一行时,会抛出一个异常,并显示消息“Caling thread无法访问对象,因为不同的线程拥有它”。上面的代码是在我相信在同一个线程上创建LayoutRoot的InitializeComponent()之后立即调用的方法中执行的。

有人请赐教。我正在尝试创建一个多UI线程WPF应用程序。

1 个答案:

答案 0 :(得分:4)

您正在反向使用CheckAccess()。你想失去!每次检查前的标志。请参阅CheckAccess() MSDN页面上的示例代码位。

在Winforms世界中,你会做一个InvokeRequired(),它现在与!CheckAccess()相同。在你的情况下,因为两个值都返回true,并且你正在反转它们,所以如果块被击中都没有。

在Winforms世界中扩展一点 ...,正常模式是:

if(InvokeRequired)
{
    Invoke(...);
}
else
{
    //do work
}

(或者有时在调用后返回,如果它正在调用相同的方法)。

在WPF中,CheckAccess()类似于InvokeRequired,但与InvokeRequired不同......对于更多类似的模式:

if (someUiControl.Dispatcher.CheckAccess())
{
    //Doing an update from this thread is safe, so we can do so here.
}
else
{
    // This thread does not have access to the UI thread.
    // Call the update thread via a Dispatcher.BeginInvoke() call.
}

之间的关键区别是InvokeRequired()返回true表示在当前线程中进行更新是UNSAFE ...而来自CheckAccess()的true表示它是SAFE。