我在SO上已经阅读了一些类似的问题,并通过this one我理解两个线程无法同时访问位图对象,这似乎是合乎逻辑的。但是,我没有找到解决方案。
我正在开发一个多线程应用程序,涉及一个相机以稳定的10 FPS速率向我的程序发送图像。我的代码使用几个后台工作程序从相机源中选择图像并从中提取数据。总的来说,它工作得很好(我开始在实际实验中使用它),但其中一个线程有时会崩溃应用程序,因为它试图访问正在其他地方使用的位图(实际上它正在被其“创建”线程刷新)。
我想找到一种方法来避免这种崩溃。我可以确保在线程尝试访问位图之前没有使用位图吗?还有其他办法吗?
在我添加代码之前,请注意我不是专业开发人员。我在三个月前开始编写的代码较少,因此我的代码可能不是最好的代码,我很乐意听取您的建议。此外,我使用的相机的API非常奇怪,我很难让事情顺利工作(请参阅我关于Vimba API的其他问题)。我不打算使用这种复杂的语法,但它是唯一一个使事情顺利运行的语法。
这是我写的代码的“精简版”:
/// Frame queueing and reception from the camera
private void OnFrameReceived(Frame frame)
{
frame.Fill(ref BitmapFromCamera);
mycamera.QueueFrame(frame);
bgw1.RunWorkerAsync();
}
/// Handles image reception from the camera : creating all the bitmaps I will need afterward
private void bgw1_DoWork(object s, DoWorkEventArgs e)
{
BMLiveCamera = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
BMDataHist = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
pictureBoxLiveCamera.Image = BMLiveCamera.Clone(cloneRect, BMLiveCamera.PixelFormat);
//There are much more that are created, but only those ones are relevant here
}
/// Does the graphic work and the maths for plotting a live-updating histogram
private void bgw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (this.InvokeRequired)
{
BeginInvoke((Action)(() => bgw1_RunWorkerCompleted(sender, e)));
}
else
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
DataFromBitmap DataFromBitmapHist = new DataFromBitmap(BMDataHist.Clone(cloneRect, BMDataHist.PixelFormat)); //THIS IS THE LINE THAT CAUSES THE CODE TO CRASH
PixelColorCount = DataFromBitmapHist.ColorCountOutput();
//I know, creating a new thread here, after making sure we were running on the UI thread is strange, but it greatly enhanced the smoothness of the execution. Th application crashed consistantly within 10 seconds of starting before I used this syntax
}).Start();
chartHist.Titles["Title2"].Visible = false;
chartHist.Series["Pixel count"].Points.Clear();
//Plotting the pixel counter, to detect saturation
for (int i = PixelColorCount.Length - 50; i < PixelColorCount.Length; i++)
{
chartHist.Series["Pixel count"].Points.AddXY(i, PixelColorCount[i]);
}
//If there are saturated pixels : toggle a title on chartHist to warn the user
if (PixelColorCount.Last() > 1)
{
chartHist.Titles["Title1"].Visible = false;
chartHist.Titles["Title2"].Visible = true;
}
else
{
chartHist.Titles["Title1"].Visible = true;
chartHist.Titles["Title2"].Visible = false;
}
}
}
正如我所说的,我认为当DataFromBitmap
类在bgw1_DoWork
更新BMDataHist
位图时运行时会发生崩溃。然而,崩溃不会发生。有时它几乎在应用程序启动后立即发生,有时它会平稳运行2个小时而不会崩溃。我已经尝试在UI中创建位图,但它使应用程序更不平滑。
有人有个主意吗?如果需要,我可以添加更多细节。
谢谢!
编辑:在评论之后,我已经摆脱了BacggroundWorker,并使用了Tasks而不是创建了Thread。如评论中所述,OnFrameReceived在单独的线程上运行 这是更新的代码:
private void OnFrameReceived(Frame frame)
{
frame.Fill(ref BitmapFromCamera);
mycamera.QueueFrame(frame);
Task.Factory.StartNew(() =>
{
lock(lockObject)
{
Thread.CurrentThread.IsBackground = true;
BMPBLiveExpClickInit = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
BMPBLiveExpClickFinal = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
MDataLiveExpClickInit = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
BMDataLiveExpClickFinal = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
BMPBTimerTickFinal = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
BMDataTimerTickFinal = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
BMPBFixInit = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
BMPBFixFinal = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
BMDataFixInit = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
BMDataFixFinal = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
pictureBoxLiveCamera.Image = BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat);
DataFromBitmap DataFromBitmapHist = new DataFromBitmap(BitmapFromCamera.Clone(cloneRect, BitmapFromCamera.PixelFormat));
PixelColorCount = DataFromBitmapHist.ColorCountOutput();
}
});
BeginInvoke((Action)(() =>
{
chartHist.Titles["Title2"].Visible = false;
chartHist.Series["Pixel count"].Points.Clear();
//Plotting the pixel counter, to detect saturation
for (int i = PixelColorCount.Length - 50; i < PixelColorCount.Length; i++)
{
chartHist.Series["Pixel count"].Points.AddXY(i, PixelColorCount[i]);
}
//If there are saturated pixels : toggle a title on chartHist to warn the user
if (PixelColorCount.Last() > 1)
{
chartHist.Titles["Title1"].Visible = false;
chartHist.Titles["Title2"].Visible = true;
}
else
{
chartHist.Titles["Title1"].Visible = true;
chartHist.Titles["Title2"].Visible = false;
}
}));
}
它运行,但崩溃仍然发生,我甚至经常说。此外,UI的响应性要低得多,如果整体应用程序整体不如以前那么平滑。我想现在代码更“正确”,但它的性能也更差。为什么?我该怎么办呢?