I have read a few similar questions here on SO, and through this one I understood that two threads cannot access a bitmap object at the same time, which seems logical. I didn"t find a solution, though.
I am working on a multi-threaded application involving a camera sending images to my program at a stable 10 FPS rate. My code uses several background workers to pick images from the camera feed and extract data from them. Overall, it works quite well (I started using it in actual experiments), but one of the threads sometime crashes the app because it tries to access a bitmap that is being used elsewhere (in fact it is being refreshed by its "creating" thread).
I would like to find a way to avoid this crash. Can I make sure that the bitmap is not beoing used before the thread tries to access it ? Is there another way to do ?
Before I add the code, please note that I am not a professional developer. I started coding less that 3 months ago, so my code is probably not the best that could have been done, and I will gladly listen to you suggestions. Also, the API from the camera I am using is VERY strange, and I had a very hard time getting things to work somewhat smoothly (see my other questions regarding the Vimba API). I didn't intend to use such a convoluted syntax, but it is the only one that made things work smoothly.
Here is a "stripped down" version of the code I wrote :
/// 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;
}
}
}
As I said, I think the crash occurs when the DataFromBitmap
class is ran at the moment bgw1_DoWork
updates the BMDataHist
bitmap. The crash doesn't happen consistantly, though. Sometime it happens almost immediately after the application starts, sometime it runs smoothly for 2 hours without crashing. I already tried creating the bitmaps in the UI thred, but it makes the application much less smooth.
Anyone got an idea ? I can add more details if needed.
Thanks !
EDIT : following the comments, I have gotten rid of the BacggroundWorker, and used Tasks instead of creating Thread. As noted in the comments, OnFrameReceived runs on a separate thread Here is the updated code :
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;
}
}));
}
It runs, but the crash still happens, more often I would even say. Moreover the UI is much less responsive, the application if overall less smooth than it was before. I guess the code is much more "correct" now, but it is also less performing. Why ? What could I do about it ?