我需要获得在两个不同的线程的锁,以便访问一个位图(这是从一个摄像头填充)在EmguCv。 我有一个查询的相机并把它返回到.NET位图“的getFrame”功能。 我有两个线程需要访问该位图,一个需要写入的位图和分配的位图图片框,以及其他需要读取位图,将其转换为一个Image对象并将其赋值给一个EMGU ImageBox。 我首先锁定在任意对象,然后我做我的作业。 的代码如下(_Camera.LiveFrame是位图):
读/写主题:
while (_CaptureThreadRunning)
{
lock (_Camera)
{
// _Camera.GetFrame writes to the Bitmap
if (_VideoPlaying && _Camera.GetFrame(500))
pbLiveFeed.Invalidate();
}
}
_Camera.CloseCamera(true);
_CaptureExitEvent.Set(); // Set to signal captureThread has finished
读/ ImageBox主题:
while (_ProcessThreadRunning)
{
lock (_Camera)
{
// _Camera.LiveFrame is the Bitmap
procImage = new Image<Bgr, int>((Bitmap)_Camera.LiveFrame.Clone());
procImage.Draw(new Rectangle(10,20,20,15),new Bgr(Color.LightGreen), 5);
ibProcessed.Image = procImage;
ibProcessed.Invalidate();
}
}
_ProcessExitEvent.Set();
这运行正常的大部分时间,但每一个现在,然后我得到一个“对象是在其他地方使用”的错误,当我尝试的clone()位图。 这难道不是锁定的正确方法? 我不明白为什么这会导致问题。
PS。 我的线程可以不再退出优雅无论是。 我.SET()调用之外的我的圈永远不会被调用。 我猜的线程死锁?
GDI +有一个锁定机制防止两个线程使用Bitmap对象 - 这是您收到的错误。
您试图访问该位图,而UI线程已经访问它。 例如,1)你的位图分配给图片框,2)图片框无效信号,然后重新绘制,3)退出写/读螺纹锁,然后4)读/ imagebox线程试图访问相同的位图而重绘仍在发生。
为了解决这个问题,只是使位图的副本,并使用该副本来操作。 无论你给的图片框,不要以为你可以从一个非UI线程再次触及这一点。
例如,在_Camera.GetFrame:
// Get the bitmap from the camera
capturedBitmap = GetFromCamera();
// Clone the bitmap first before assigning to the picture box
_Camera.LiveFrame = new Bitmap(capturedBitmap);
// Assign to the picture box
pbLiveFeed.Image = capturedBitmap;
现在,_Camera.LiveFrame应该从线程访问,只要你有适当的锁定。
一对夫妇的我会考虑这里的其他问题:
你提到你是一个“任意对象”上锁定,但_Camera似乎是什么,但 - 它是在其他地方不可预知的方式被使用的对象。 我建议做这仅用于锁定的对象,如
object lockObject = new lockObject; lock (lockObject) { // put your synchronized code here }
Bitmap.Clone()创建这股像素数据与原来的位图的位图。 当您将转换为图像对象分配给EMGU ImageBox,您使用的是克隆,它保持位图的参考。 因此,它似乎更安全,我只需要创建一个新的位图,而不是在这种情况下使用的clone()。
我认为你能避免使用在这里明确的锁都没有。 只要将位图创建操作来接收线程 - 这种方式,您将保证对原始位的所有操作都在接收线程执行。
一旦创建位图完成,传递给新位图参考阅读话题 - 把它分配给类服务它的成员。 参考分配是一个原子操作,这样保证了读取线程将看到此新值或旧的。 和而你只传授你完成创建位图后的参照,这样保证了只有读线程将永远使用它
您可以使用的ManualResetEvent而不是锁来编排的读操作和写。 一个例子是这样的。
读/写主题:
while (_CaptureThreadRunning)
{
imageBoxTrhead.WaitOne();
readWriteThread.Reset();
// _Camera.GetFrame writes to the Bitmap
if (_VideoPlaying && _Camera.GetFrame(500))
pbLiveFeed.Invalidate();
readWriteThread.Set();
}
_Camera.CloseCamera(true);
_CaptureExitEvent.Set();
读/ ImageBox主题:
while (_ProcessThreadRunning)
{
readWriteThread.WaitOne();
imageBoxTrhead.Reset();
// _Camera.LiveFrame is the Bitmap
procImage = new Image<Bgr, int>((Bitmap)_Camera.LiveFrame.Clone());
procImage.Draw(new Rectangle(10,20,20,15),new Bgr(Color.LightGreen), 5);
imageBoxTrhead.Set();
ibProcessed.Image = procImage;
ibProcessed.Invalidate();
}
_ProcessExitEvent.Set();
凡readWriteThread和imageBoxTrhead默认情况下ManualResetEvent的对象发出信号。