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

Posted

技术标签:

【中文标题】调用线程无法访问此对象,因为另一个线程拥有它[重复]【英文标题】:The calling thread cannot access this object because a different thread owns it [duplicate] 【发布时间】:2011-02-13 07:22:57 【问题描述】:

为什么我不能在下面的代码中创建 CroppedBitmap?我遇到了一个例外:

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

如果我将代码更改为

CroppedBitmap cb = new CroppedBitmap(new WriteableBitmap(bf), new Int32Rect(1, 1, 5, 5));

异常消失了吗?为什么?

代码 1,cb.Freeze() 处的异常:

public MainWindow()

    InitializeComponent();

    ThreadPool.QueueUserWorkItem((o) =>
        
            //load a large image file
            var bf = BitmapFrame.Create(
                new Uri("D:\\1172735642.jpg"),
                BitmapCreateOptions.None,
                BitmapCacheOption.None);
            bf.Freeze();
            Dispatcher.BeginInvoke(
                new Action(() =>
                    
                        CroppedBitmap cb = new CroppedBitmap(bf, new Int32Rect(1,1,5,5));
                        cb.Freeze();
                        //set Image's source to cb....
                    ), 
                    DispatcherPriority.ApplicationIdle);
         
    );

代码 2,有效:

    ThreadPool.QueueUserWorkItem((o) =>
    
        var bf = BitmapFrame.Create(
                new Uri("D:\\1172740755.jpg"),
                BitmapCreateOptions.None,
                //BitmapCreateOptions.DelayCreation,
                BitmapCacheOption.None);
        bf.Freeze();
        var wb = new WriteableBitmap(bf);
        wb.Freeze();
        this.Dispatcher.Invoke(
            new Action(() =>
            
                var r = new Int32Rect(1, 1, 5, 5);
                CroppedBitmap cb = new CroppedBitmap(wb, r);
                cb.Freeze();
                //set Image's source to cb....
                Image.Source = cb;
            ),
            DispatcherPriority.ApplicationIdle);
    
);

代码 3,无需 WritableBitmap 即可工作:

ThreadPool.QueueUserWorkItem((o) =>
    
        var bf = BitmapFrame.Create(
                new Uri("D:\\1172735642.jpg"),
                BitmapCreateOptions.None,
                //BitmapCreateOptions.DelayCreation,
                BitmapCacheOption.None);
        bf.Freeze();
        var bf2 = BitmapFrame.Create(bf);
        bf2.Freeze();

        this.Dispatcher.Invoke(
            new Action(() =>
            
                var r = new Int32Rect(1, 1, 5, 5);
                BitmapSource cb = new CroppedBitmap(bf2, r);
                cb.Freeze();
                //set Image's source to cb....
                Image.Source = cb;
            ),
            DispatcherPriority.ApplicationIdle);
    
);

【问题讨论】:

BitmapFrame 闻起来像一个 UI 类,它是在工作线程上创建的,然后在 UI 线程上使用... 【参考方案1】:

以下代码可能会帮助您解决从另一个线程更新 gui 元素的问题:

模块级

delegate void updateCallback(string tekst);

这是更新元素的方法:

private void UpdateElement(string tekst)

    if (element.Dispatcher.CheckAccess() == false)
    
        updateCallback uCallBack = new updateCallback(UpdateElement);
        this.Dispatcher.Invoke(uCallBack, tekst);
    
    else
     
//update your element here
    
 

【讨论】:

我使用它并得到错误:“无法使用属于与其父 Freezable 不同的线程的 DependencyObject。” 很好的实现。易于使用。【参考方案2】:

使用 WPF 时请注意,如果您在一个线程中创建 UI 对象,则无法从另一个线程访问它。您的 UI 对象应该(通常)由 UI 线程创建,然后您需要 UI 线程稍后访问它们。没有其他线程能够访问在 UI 线程上创建的对象。

如果您需要从另一个线程访问 UI 对象,则需要 UI 线程 Dispatcher,然后您可以使用它来调用 UI 线程上的调用。

我已经花了很多时间来解决与此类似的问题 - 相信我.. 看看 this question - 它给了我很多关于这个主题的有用信息。

【讨论】:

但是,在我将代码更改为 CroppedBitmap cb = new CroppedBitmap(new WriteableBitmap(bf), new Int32Rect(1, 1, 5, 5));没有例外。为什么?他们仍然在不同的线程中。 抱歉 - 我不确定。没用过WriteableBitmap。也许文档可以给你一个提示? msdn.microsoft.com/en-us/library/…【参考方案3】:

您可以在反射器中查看这些类。 cb.Freeze() 中会出现异常。在

CroppedBitmap cb = new CroppedBitmap(bf, new Int32Rect(1,1,5,5));

case 构造函数做了这样的事情:

this.this.Source = source;

所以源不是在当前线程中创建的,所以会出现异常。 在

new WriteableBitmap(bf)

情况,构造函数与 bf 对象同步,并且在当前线程中创建了新源,因此不会出现异常。 如果您对 In Depth 细节感兴趣,您可以随时使用 Reflector 反射基础库 :)

【讨论】:

我更新了我的代码,请看我的新代码 2,CroppedBitmap cb = new CroppedBitmap(wb, r); wb 现在在不同的线程中,但没有例外。为什么? 没有抛出异常,因为 WriteableBitmap 是为与多个线程一起工作而创建的(它甚至具有锁定和解锁方法)msdn.microsoft.com/en-en/library/… 而 CroppedBitmap - 实际上并不是,起初我认为只有 BitmapFrame 可以'不喜欢从多个线程调用,我错了,CroppedBitmap 不喜欢它:) 我又更新了我的代码,请看我的新代码3,现在我用BitmapFrame替换了WritableBitmap,它仍然有效。 BitmapFrame乍一看好像一样,其实不同的构造函数创建的类不同(BitmapFrame其实是抽象的),bf是BitmapFrameDecode,bf2是BitmapFrameEncode,它们是BitmapFrame派生的内部类,所以一个是支持访问当另一个不是时,来自另一个线程。实际上有些属性有复杂的 getter 会抛出异常,所以如果你对它感兴趣,去去反汇编 :) 谢谢,我会尝试使用反射器跟踪代码,但我不知道如何判断位图类是否支持线程。【参考方案4】:

我遇到了同样的问题,并通过使用它的调度程序在 UI 线程中创建我的UIElement 解决了这个问题(可以通过 Application.Current.Dispatcher 访问)。

之前:

public static UIElement CreateUIElement()

    UIElement element = new UIElement();
    //Initialized the UIElement here
    return element;

此代码导致 XamlParseException,因为它是在与 UI 线程不同的线程中调用的。

我的工作解决方案:

public static UIElement CreateUIElement()

    UIElement element = null;
    Application.Current.Dispatcher.Invoke(
       System.Windows.Threading.DispatcherPriority.Normal, new Action(
          delegate()
          
              element = new UIElement();
              // Initialize your UIElement here
          ));
    return element;

可以在此处找到有关调度程序的更多信息 http://tech.pro/tutorial/800/working-with-the-wpf-dispatcher

祝你好运

【讨论】:

以上是关于调用线程无法访问此对象,因为另一个线程拥有它[重复]的主要内容,如果未能解决你的问题,请参考以下文章

调用线程无法访问此对象 - Timer [重复]

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

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

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

“调用线程无法访问此对象,因为不同的线程拥有它”从 WPF 中的不同线程更新 UI 控件时出现错误

Wpf中“由于其他线程拥有此对象,因此调用线程无法对其进行访问”