我一直在努力寻找正确的答案,而我所发现的一切要么太复杂,要么就是没有按照我的期望去做。 情况很简单:
我已经尝试过后台工作者,但是任务重叠,因为我找不到可靠的方法来检查完成情况。我已经尝试运行任务,但是无法重新启动它们。我已经尝试过使用async / await,但是我不想等待完成。
编辑: 我将提供更多信息。该应用程序用于面部识别软件。我必须处理3台摄像机,并且正在使用EmguCV。每个摄像机都订阅一个名为“ ProcessFrame”的ImageGrabbed事件,所以我有ProcessFrame1,ProcessFrame2和ProcessFrame3。在给出的情况下,事件几乎以每个摄像机的fps触发,因此频率很高。在每个事件中,我都进行捕获并将其显示在ImageBox(Emgu的pictureBox)中。每拍摄5张照片,我会检查每个相机是否至少拍摄一张照片,在这种情况下,我会在每个图像上进行人脸识别,而不是将其显示在图像框中。这是我要在单独的任务中执行的任务,以避免停止每个摄像机的实时视频。
现在,正如某些人建议的那样,我正在尝试使用信号灯,尽管我在设置机会在三个事件中执行DetectFace()时遇到了一些麻烦,所以我只剩下一个。 这是一个片段:
public Form1()
{
InitializeComponent();
//Instantiate each camera
//Subscribe to ProcessFrame1, ProcessFrame2 and ProcessFrame3
}
private void ProcessFrame1(object sender, EventArgs e)
{
if (captures[0] != null) //captures[0] is the handle for the camera 1
{
Mat snapshot = new Mat();
captures[0].Retrieve(snapshot);
if (snapshot != null)
{
frameCounter1++;
if (frameCounter1 > 5 && taskCompleted)
{
frameCounter1 = 0;
if (images[0] == null)
{
Image<Bgr, Byte> img = snapshot.ToImage<Bgr, Byte>();
images[0] = img.ToBitmap();
}
if (images[0] != null && images[1] != null && images[2] != null)
{
Thread hilo = new Thread(() => DetectFace());
hilo.IsBackground = true;
hilo.Start();
}
return;
}
else
imageBox1.Image = snapshot;
}
}
}
private void ProcessFrame2(object sender, EventArgs e)
{
if (captures[1] != null) //captures[1] is the handle for the camera 2
{
Mat snapshot = new Mat();
captures[1].Retrieve(snapshot);
if (snapshot != null)
{
frameCounter2++;
if (frameCounter2 > 5 && taskCompleted)
{
frameCounter2 = 0;
if (images[1] == null)
{
Image<Bgr, Byte> img = snapshot.ToImage<Bgr, Byte>();
images[1] = img.ToBitmap();
}
//I used to have the checking to fire up another DetectFace here
return;
}
else
imageBox2.Image = snapshot;
}
}
}
private void ProcessFrame3(object sender, EventArgs e) //Same as ProcessFrame2
private void DetectFace()
{
taskCompleted = false;
//Processing of Images
//Clear array of images
taskCompleted = true;
}
答案 0 :(得分:0)
任务在状态下工作,因此您可以随时启动任务,将引用保存在变量中,并随时检查任务的当前状态。
这是.NET文档,用以阅读任务的不同状态:
遗憾的是,目前我无法给您提供代码示例,但我希望这个想法能对您有所帮助。
答案 1 :(得分:0)
我终于使用BlockingCollection实现了线程安全解决方案。感谢@Damien_The_Unbeliever指出正确的方向。
我认为在多线程环境中使用图像时这是一个重要的教训。我了解到,在不同线程中共享图像时,它们非常容易受到攻击。
正如我在问题中发布的那样,在这里,我必须从X个不同的网络摄像头中拍摄快照,并在X个不同的imageBox中显示它们,并以最快的速度(不会过多中断所显示视频的fps)来执行图像处理而不是在imageBox中显示框架。使用BlockingCollection,我不需要像以前一样稳定处理帧的频率(每5帧)。现在,只要已经将来自该摄像机的帧添加到BlockingCollection中,就可以显示每个摄像机的帧。
要注意的另一个重要细节是,在.NET文档中,BlockingCollection表示默认情况下它实现FIFO,因为它是较低层中的ConcurrentQueue,但是我认为这不是正确的,因为我必须定义实例化时是我自己的:
BlockingCollection<Tuple<int, Image>> tupleCollection = new BlockingCollection<Tuple<int, Image>>(new ConcurrentQueue<Tuple<int, Image>>(), X);
由于Take()方法无法将集合中的所需元素作为目标,因此我不得不使用元组来知道帧属于哪个摄像机,并采取这些帧以便必须定义ConcurrentQueue。 / p>
所以基本上伪代码是这样的:
void Main()
{
//Instantiate cameras
//Subscribe to the ImageGrabbed events of each (producers)
// A simple blocking consumer with no cancellation.
Task.Run(() => DetectFace());
}
producer1(sender, e)
{
//Get snapshot
...
if (!tupleCollection.Any(x => x.Item1 == 1))
{
tupleCollection.Add(new Tuple<int, Image>(1, snapshot));
}
else
imageBox1.Image = snapshot;
}
producer2(sender, e)
{
//Get snapshot
...
if (!tupleCollection.Any(x => x.Item1 == 2))
{
tupleCollection.Add(new Tuple<int, Image>(2, snapshot));
}
else
imageBox2.Image = snapshot;
}
...
producerX(sender, e)
{
//Get snapshot
...
if (!tupleCollection.Any(x => x.Item1 == X))
{
tupleCollection.Add(new Tuple<int, Image>(X, snapshot));
}
else
imageBoxX.Image = snapshot;
}
private void DetectFace()
{
while (true)
{
Tuple<int, Image> data = null;
try
{
data = tupleCollection.Take();
}
catch (InvalidOperationException) { }
if (data != null)
{
//image processing
}
}
}
我发现大多数示例都使用条件IsCompletedAdded和IsCompleted来停止添加和使用,但是我需要它一直运行,因此while(true)语句。
上周我一直在24/7上运行此代码,到目前为止没有出现赛车故障,并且CPU处理器非常有限,所以我对该解决方案非常满意,我认为这是正确的解决方案。