启动异步任务而不等待它完成,但是检查完成情况

时间:2019-02-15 10:23:36

标签: c# winforms

我一直在努力寻找正确的答案,而我所发现的一切要么太复杂,要么就是没有按照我的期望去做。 情况很简单:

  1. 我想不时地异步启动任务。总是相同的任务。没有定义的启动时间间隔(假设它是随机的)。
  2. 任务不带任何参数,也不返回任何内容。
  3. 我不想等待它完成。它不会干扰程序的其余部分。
  4. 我想在再次发射之前检查是否完成。不想使同一任务同时运行多次。每次只有一个。
  5. 我不想使用计时器或信号灯之类的全局变量。只是一个非常简单的问题的简单而干净的解决方案。

我已经尝试过后台工作者,但是任务重叠,因为我找不到可靠的方法来检查完成情况。我已经尝试运行任务,但是无法重新启动它们。我已经尝试过使用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;
}

2 个答案:

答案 0 :(得分:0)

任务在状态下工作,因此您可以随时启动任务,将引用保存在变量中,并随时检查任务的当前状态。

这是.NET文档,用以阅读任务的不同状态:

https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.status?view=netframework-4.7.2

https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskstatus?view=netframework-4.7.2

遗憾的是,目前我无法给您提供代码示例,但我希望这个想法能对您有所帮助。

答案 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处理器非常有限,所以我对该解决方案非常满意,我认为这是正确的解决方案。