Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 启动异步任务而不等待它完成,但检查是否完成_C#_Winforms - Fatal编程技术网

C# 启动异步任务而不等待它完成,但检查是否完成

C# 启动异步任务而不等待它完成,但检查是否完成,c#,winforms,C#,Winforms,我一直在努力寻找一个正确的答案,我发现要么太复杂,要么就是没有做我想要的。 情况很简单: 我想时不时地异步启动一个任务。同样的任务总是一样的。没有定义启动它的时间间隔(假设它是随机的) 该任务不接受任何参数,也不返回任何内容 我不想等它结束。它不能干扰程序的其余部分 我想在再次发射之前检查它是否完成。不希望同一任务同时运行多次。每次一个 我不想使用计时器或全局变量,比如信号量之类的。这只是一个简单问题的简单明了的解决方案 我尝试过背景工作,但任务重叠,因为我找不到可靠的方法来检查完成情况。我已尝

我一直在努力寻找一个正确的答案,我发现要么太复杂,要么就是没有做我想要的。 情况很简单:

  • 我想时不时地异步启动一个任务。同样的任务总是一样的。没有定义启动它的时间间隔(假设它是随机的)
  • 该任务不接受任何参数,也不返回任何内容
  • 我不想等它结束。它不能干扰程序的其余部分
  • 我想在再次发射之前检查它是否完成。不希望同一任务同时运行多次。每次一个
  • 我不想使用计时器或全局变量,比如信号量之类的。这只是一个简单问题的简单明了的解决方案
  • 我尝试过背景工作,但任务重叠,因为我找不到可靠的方法来检查完成情况。我已尝试运行任务,但无法重新启动。我已尝试使用async/await,但我不想等待完成

    编辑: 我会提供更多的信息。此应用程序用于面部识别软件。我要处理3台摄像机,我用的是EmguCV。每个摄像头都使用一个名为“ProcessFrame”的ImageGrapped事件,因此我有ProcessFrame1、ProcessFrame2和ProcessFrame3。事件几乎以每台摄像机的fps触发,因此,频率非常高。在每一次活动中,我都会拍摄一张照片,并将其显示在ImageBox(Emgu的pictureBox)中。每捕获5次,我会检查每个摄像头是否至少有一次捕获,在这种情况下,我不会在imagebox中显示它,而是对每个图像执行面部识别。这是我想在单独的任务中执行的任务,以避免停止每个摄像头的实时视频

    现在我正在尝试使用一个信号量,正如你们中的一些人所建议的,尽管我在设置在三个事件中执行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;
    }
    
    public Form1()
    {
    初始化组件();
    //实例化每个摄像头
    //订阅ProcessFrame1、ProcessFrame2和ProcessFrame3
    }
    私有void ProcessFrame1(对象发送方,事件参数e)
    {
    if(captures[0]!=null)//captures[0]是相机1的句柄
    {
    Mat快照=新Mat();
    捕获[0]。检索(快照);
    如果(快照!=null)
    {
    frameCounter1++;
    如果(frameCounter1>5&&taskCompleted)
    {
    帧计数器1=0;
    如果(图像[0]==null)
    {
    Image img=snapshot.ToImage();
    images[0]=img.ToBitmap();
    }
    如果(图像[0]!=null和图像[1]!=null和图像[2]!=null)
    {
    线程hilo=新线程(()=>DetectFace());
    hilo.IsBackground=true;
    hilo.Start();
    }
    回来
    }
    其他的
    imageBox1.Image=快照;
    }
    }
    }
    私有void ProcessFrame2(对象发送方,事件参数e)
    {
    if(captures[1]!=null)//captures[1]是照相机2的手柄
    {
    Mat快照=新Mat();
    捕获[1]。检索(快照);
    如果(快照!=null)
    {
    frameCounter2++;
    如果(frameCounter2>5&&taskCompleted)
    {
    帧计数器2=0;
    如果(图像[1]==null)
    {
    Image img=snapshot.ToImage();
    images[1]=img.ToBitmap();
    }
    //我曾经有一张支票在这里点燃另一张侦探脸
    回来
    }
    其他的
    imageBox2.Image=快照;
    }
    }
    }
    private void ProcessFrame3(对象发送方,EventArgs e)//与ProcessFrame2相同
    私有void DetectFace()
    {
    taskCompleted=false;
    //图像处理
    //清晰的图像阵列
    taskCompleted=true;
    }
    
    任务使用状态,因此您可以随时启动任务、将引用保存在变量中并检查任务的当前状态

    以下是有关任务不同状态的.NET文档:


    遗憾的是,我现在不能给你一个代码示例,但我希望这个想法能有所帮助

    我最终使用BlockingCollection实现了线程安全解决方案。感谢不信者@Damien_为我们指明了正确的方向

    我认为这是在多线程环境中处理图像的一个重要教训。我了解到,在不同的线程中共享图像时,它们非常容易受到攻击

    正如我在问题中所说,这里我必须从X个不同的网络摄像头中拍摄快照,在X个不同的图像框中显示它们,并尽可能快地(在不太中断所显示视频的fps的情况下)执行图像处理,而不是在图像框中显示帧。使用BlockingCollection,我不需要像以前那样(每5帧)确定处理帧的频率。现在我可以显示每个相机的帧,只要我已经将该相机的帧添加到BlockingCollection中

    另一个需要注意的重要细节是,在BlockingCollection的.NET文档中,它说默认情况下它实现了FIFO,因为它是下层的ConcurrentQueue,但我认为这不是真的,因为我在实例化时必须自己定义它:

    BlockingCollection<Tuple<int, Image>> tupleCollection = new BlockingCollection<Tuple<int, Image>>(new ConcurrentQueue<Tuple<int, Image>>(), X);
    
    BlockingCollection tupleCollection=new BlockingCollection(new ConcurrentQueue(),X);
    
    由于Take()方法无法针对集合中所需的元素,因此我必须使用元组来知道帧属于哪个摄影机,并按照定义ConcurrentQueue的顺序获取帧

    所以基本上伪代码是这样的:

    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
          }
       }
    }
    
    void Main()
    {
    //实例化摄影机
    //订阅