尝试从其他胎面更新图像控制源并获取错误

时间:2011-11-20 11:47:53

标签: c# wpf multithreading dispatcher

我的项目是关于捕获全屏并通过此图像更新图像控制;

在最后一行(image1.Source = img;)我收到错误:

The calling thread cannot access this object because a different thread owns it.

代码:

public partial class MainWindow : Window
{

    delegate void  MyDel(BitmapImage img);

    Queue<BitmapImage> picQueue = new Queue<BitmapImage>();

    public MainWindow()
    {
        InitializeComponent();

        Thread updateTrd = new Thread(new ThreadStart(UpdateQueue));
        updateTrd.Start();

        Thread PicTrd = new Thread(new ThreadStart(UpdateScreen));
        PicTrd.Start();
    }

    private void UpdateQueue()
    {
        while (true)
        {
            ScreenCapture sc = new ScreenCapture();//this function provide a desktop screenshot
            System.Drawing.Image img = sc.CaptureScreen();
            Stream stream = new MemoryStream();
            img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);

            BitmapImage image = new BitmapImage();
            image.BeginInit();
            image.StreamSource = stream;
            image.EndInit();

            picQueue.Enqueue(image);
        }
    }

    private void UpdateScreen()
    {
        while (true)
        {
            if (picQueue.Count > 0)
            {
                SwitchPic(picQueue.Dequeue());
            }
            else
            {
                Thread.Sleep(30);
            }
        }
    }

    private void SwitchPic(BitmapImage img)
    {
        if(!image1.Dispatcher.CheckAccess())
        {
            this.image1.Dispatcher.BeginInvoke(new MyDel(SwitchPic), img);
        }
        else
        {
            image1.Source = img;
        }
    }
}

5 个答案:

答案 0 :(得分:2)

解决方案

传递到SwitchPic的图像由另一个线程拥有。只需将行if(!image1.Dispatcher.CheckAccess())更改为if(!img.Dispatcher.CheckAccess())

private void SwitchPic(BitmapImage img)
{
    if(!img.Dispatcher.CheckAccess())
    {
        this.image1.Dispatcher.BeginInvoke(new MyDel(SwitchPic), img);
    }
    else
    {
        image1.Source = img;
    }
}

如何改善

让我们先从那些循环中取出那些循环,因为它们会占用你的CPU。

  • 而不是围绕UpdateQueue方法包裹while循环, 然后创建一个Timer
  • 不使用Queue<T>,而是使用BlockingCollection<T> 这是为了并发访问 - 这样你就消除了 第二个无限循环。

以上实际上是producer/consumer pattern

的配方
  • Timer使用的主题是我们的制作者,因为添加 收藏品。
  • 调用UpdateScreen的线程是我们的使用者,因为它 从集合中删除项目。

您的代码示例已使用此模式(有点),但是当集合中没有项目时,它无法阻止该线程。相反,你正在做Thread.Sleep(30),与Take中使用BlockingCollection<T>方法阻止线程相比,它具有巨大的性能开销。

此外,我们可以简单地删除SwitchPic方法,并将其合并到UpdateScreen中,因为将此作为单独的方法没有任何意义 - 它只能从{{1}调用方法。

我们不必在图像上检查UpdateScreen,因为图像始终由CheckAccess使用的线程创建(Timer使用的线程是特殊的线程,因此不能被其他人使用)。此外,Timer在专用线程上运行,无需UpdateScreen

由于我假设您希望图片按顺序显示,我使用的是CheckAccess而不是Dispatcher.Invoke

然后代码如下:

Dispathcer.BeginInvoke

答案 1 :(得分:0)

由于您的picQueue is created on main thread因此队列和出列操作引发错误。将此操作放在主线程的dipatcher上以重新启动thread affinity

答案 2 :(得分:0)

您应该在Window中使用Dispatcher属性。 Invoke方法有助于将代码交换到GUI线程。类似的东西:

Dispather.Invoke(()=>{ YourCodeinMainTreadHere();})

答案 3 :(得分:0)

你可以创建一个全局变量theBitmap,然后代替设置image1.Source = img;在同一行设置theBitmap = img;然后使用计时器

private void timer1_Tick(object sender, EventArgs e)
    {
         image1.source = theBitmap
    }

答案 4 :(得分:0)

如果您正在使用线程,那么我建议您使用静态资源(对象数据提供程序)来更新另一个线程中的值。

因为静态资源可用于所有线程,并且您可以从任何线程更改它们的值。并将图像的图像属性绑定到此静态资源。当静态资源更新时,它也会更新图像。我用这种方式来更新进度条值,所以我认为它也适用于此。