我的项目是关于捕获全屏并通过此图像更新图像控制;
在最后一行(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;
}
}
}
答案 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)
如果您正在使用线程,那么我建议您使用静态资源(对象数据提供程序)来更新另一个线程中的值。
因为静态资源可用于所有线程,并且您可以从任何线程更改它们的值。并将图像的图像属性绑定到此静态资源。当静态资源更新时,它也会更新图像。我用这种方式来更新进度条值,所以我认为它也适用于此。