本文介绍了试图更新从其他胎面和得到错误图像控制源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的项目是捕捉全屏和更新该图像的图像控制;



在最后一行( image1.Source = IMG; )我得到的错误:

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



中的代码:

 公共部分类主窗口:窗口
{

委托无效MyDel(IMG的BitmapImage);

问答LT; BitmapImage的GT&; picQueue =新队列<&的BitmapImage GT;();

公共主窗口()
{
的InitializeComponent();

螺纹updateTrd =新主题(新的ThreadStart(UpdateQueue));
updateTrd.Start();

螺纹PicTrd =新主题(新的ThreadStart(UpdateScreen));
PicTrd.Start();
}

私人无效UpdateQueue()
{
,而(真)
{
抓屏SC =新的抓屏(); //此功能提供了一个桌面截图
为System.Drawing.Image IMG = sc.CaptureScreen();
流流=新的MemoryStream();
img.Save(流,System.Drawing.Imaging.ImageFormat.Bmp);

的BitmapImage形象=新的BitmapImage();
image.BeginInit();
image.StreamSource =流;
image.EndInit();

picQueue.Enqueue(图片);
}
}

私人无效UpdateScreen()
{
,而(真)
{
如果(picQueue.Count大于0)
{
SwitchPic(picQueue.Dequeue());
}
,否则
{
Thread.sleep代码(30);
}
}
}

私人无效SwitchPic(BitmapImage的IMG)
{
如果(!image1.Dispatcher.CheckAccess())
{
this.image1.Dispatcher.BeginInvoke(新MyDel(SwitchPic),IMG);
}
,否则
{
image1.Source = IMG;
}
}
}


解决方案

解决方案



传入 SwitchPic 被另一个线程拥有。简单地改变行如果(!image1.Dispatcher.CheckAccess())如果(!img.Dispatcher.CheckAccess())

 私人无效SwitchPic(BitmapImage的IMG)
{
如果(!img.Dispatcher .CheckAccess())
{
this.image1.Dispatcher.BeginInvoke(新MyDel(SwitchPic),IMG);
}
,否则
{
image1.Source = IMG;
}
}






如何提高



让我们开始得到那些while循环出的方式,因为他们会吃你的CPU。




  • 而不是周围包裹你的 UpdateQueue 方法,while循环,
    ,然后创建一个定时

  • 而不是使用的问答LT; T> ,然后用 BlockingCollection< T> 这是并发访问取得
    - 这种方式,您消除
    第二无限while循环



上面的其实是一个:




  • 定时器使用的线程是我们的的制作的,因为它的补充
    项目的集合。

  • 调用线程 UpdateScreen 是我们的消费者的,因为它
    删除的项目从集合。



您的代码示例已经使用这种模式(有点),但是它不能够阻塞线程没有项目的收藏。相反,你正在做的 Thread.sleep代码(30)其中一个富人性能比较庞大的开销,相比简单的阻塞与挑线从方法 BlockingCollection< T>



进一步,我们可以简单地删除 SwitchPic 方法,并将其合并到 UpdateScreen ,因为它并没有真正意义上有这个作为一个单独的方法 - 它只是从调用 UpdateScreen 方法。



我们不必检查的checkAccess 再在图像上,因为图像总是由线程创建的定时用途(线程的定时采用的是一种特殊的螺纹,并且因此不能由其他人使用)。此外,该 UpdateScreen 运行在一个专用的线程,消除了的checkAccess 的需求。



由于我假设你想要的图片来显示,以中,我使用 Dispatcher.Invoke ,而不是 Dispathcer 。.BeginInvoke



然后,代码如下:

 使用System.IO;使用System.Windows 
;
使用的System.Threading;
使用System.Windows.Media.Imaging;
使用System.Collections.Concurrent;
使用系统;

命名空间WpfApplication3
{
公共部分类主窗口:窗口
{
私人BlockingCollection<&的BitmapImage GT;照片=新BlockingCollection<&的BitmapImage GT;();

公共主窗口()
{
的InitializeComponent();

变种takeScreen =新的Timer(O => TakeScreenshot(),NULL,0,10);
新主题(新的ThreadStart(UpdateScreen))启动();
}

私人无效TakeScreenshot()
{
变种SC =新的抓屏();
变种IMG = sc.CaptureScreen();

变种流=新的MemoryStream();
img.Save(流,System.Drawing.Imaging.ImageFormat.Bmp);

变种形象=新的BitmapImage();
image.BeginInit();
image.StreamSource =流;
image.EndInit();

pictures.Add(图片);
}

私人无效UpdateScreen()
{
,而(真)
{
VAR项目= pictures.Take(); //如果块计数== 0
item.Dispatcher.Invoke(新动作(()=> image1.Source =项));
}
}
}
}


My project is about capturing the full screen and updating the image control by this images;

At the last line (image1.Source = img;) I get the error:

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

the code:

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;
        }
    }
}
解决方案

The solution

The image passed into SwitchPic is owned by another thread. Simply change the line if(!image1.Dispatcher.CheckAccess()) to 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;
    }
}


How to improve

Let's start with getting those while loops out of the way, since they'll eat your CPU.

  • Instead of wrapping a while loop around your UpdateQueue method,then create a Timer.
  • Instead of using a Queue<T>, then use a BlockingCollection<T>which is made for concurrent access - this way you eliminate thesecond infinity while loop.

The above is actually the recipe of a producer/consumer pattern:

  • The thread used by the Timer is our producer, since it addsitems to the collection.
  • The thread that invokes UpdateScreen is our consumer, since itremoves items from the collection.

Your code example already uses this pattern (kinda), however it's not able to block the thread when no items are in the collection. Instead you're doing Thread.Sleep(30) which haves a huge perfomance overhead, compared to simply blocking the thread with Take method from BlockingCollection<T>.

Further more, we can simply remove the SwitchPic method, and merge it into UpdateScreen, since it makes no real sense to have this as a seperate method - it's only invoked from the UpdateScreen method.

We don't have to check for CheckAccess anymore on the image, because the image is always created by the thread the Timer uses (The thread the Timer uses is a special thread, and can therefore not be used by anyone else). Also that the UpdateScreen runs on a dedicated thread, eliminates the need for the CheckAccess.

As I assume that you want the images to display in order, I'm using Dispatcher.Invoke and not Dispathcer.BeginInvoke.

The code then looks like:

using System.IO;
using System.Windows;
using System.Threading;
using System.Windows.Media.Imaging;
using System.Collections.Concurrent;
using System;

namespace WpfApplication3
{
    public partial class MainWindow : Window
    {
        private BlockingCollection<BitmapImage> pictures = new BlockingCollection<BitmapImage>();

        public MainWindow()
        {
            InitializeComponent();

            var takeScreen = new Timer(o => TakeScreenshot(), null, 0, 10);
            new Thread(new ThreadStart(UpdateScreen)).Start();
        }

        private void TakeScreenshot()
        {
            var sc = new ScreenCapture();
            var img = sc.CaptureScreen();

            var stream = new MemoryStream();
            img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);

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

            pictures.Add(image);
        }

        private void UpdateScreen()
        {
            while (true)
            {
                var item = pictures.Take(); // blocks if count == 0
                item.Dispatcher.Invoke(new Action(() => image1.Source = item));
            }
        }
    }
}

这篇关于试图更新从其他胎面和得到错误图像控制源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-15 08:31