问题描述
我的项目是捕捉全屏和更新该图像的图像控制;
在最后一行( 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 aTimer
. - Instead of using a
Queue<T>
, then use aBlockingCollection<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));
}
}
}
}
这篇关于试图更新从其他胎面和得到错误图像控制源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!