在多线程相机帧就绪事件中使用全局var

本文关键字:全局 var 事件 多线程 相机 就绪 | 更新日期: 2023-09-27 18:21:51

我正在编写一个依赖于快速图像操作的应用程序。这听起来可能很奇怪,但我是用C#做的,而不是用C++。到目前为止,这还没有限制,我可以实时处理图像。虽然我对图像做了一些相当复杂的事情,而且我在30毫秒内完成。

我更改了程序以确保图像流永远不会排队通过简单地检查布尔值来检查当前帧是否未被处理。通常情况下不会发生这种情况,但在某些情况下确实发生了。例如,当在VS2010调试模式下运行应用程序时,或者当PC也在执行其他繁重的任务,并且CPU资源较少时。

在这种情况下,我希望跳过新帧处理,这样处理它们就不会排队。在这种情况下,最好只处理仍在处理中的最后一个已知数据,因此等待将是检索答案的最快方法。

所以我开始做这样的事情:

private void Camera_FrameReady(object Sender, ImageEvent e)
{
  if (!IsImageReady) return;  // global var
  IsImageReady = false;
  //... do stuff with the image
  IsImageReady=true;
}

这并没有像我所希望的那样锻炼身体。我认为这与C#编译器中事件的线程性质有关。因此,我试图通过取消注册并重新注册Camera_FrameReady来解决这个问题,但相机需要很长时间才能重新启动,所以这并没有起到作用。

奇怪的是,现在它可以与下面的代码一起工作,但我不确定为什么会这样。

private void Camera_FrameReady(object Sender, ImageEvent e)
{
   Update_Image(e)
}
private void Update_Image(e)
{
  if (!IsImageReady) return;  // global var
  IsImageReady = false;
  //... do stuff with the image
  IsImageReady=true;
}

这让我想知道C#是如何编译的。无论何时调用Camera_FrameReady,它都对当前的全球价值观有"世界观"吗?或者全局变量只在事件处理后更新?

在多线程相机帧就绪事件中使用全局var

我想到的第一件事是Camera_FrameReady事件阻塞了采集线程。但这并不能解释为什么第二种解决方案有效。。


因此,如果你想与采集线程并行处理图像,你应该创建一个新的线程进行处理。


例如:当有一个新图像时,检查处理线程是否繁忙。如果处理线程正忙,您不应该等待或排队(就像您想要的那样),而应该跳过此图像。如果处理线程正在等待工作,请将图像存储在"全局"变量中,以便处理线程可以访问它并向处理线程发出信号。

我给你举了一个例子:(伪)

    // the thread for the processing.
    private Thread _processingThread;
    // a signal to check if the workerthread is busy with an image
    private ManualResetEvent _workerThreadIsBusy = new ManualResetEvent(false);
    // request for terminating
    private ManualResetEvent _terminating = new ManualResetEvent(false);
    // confirm terminated
    private ManualResetEvent _terminated = new ManualResetEvent(false);
    // store the current image.
    private Image _myImage;
    // event callback for new images
    private void Camera_FrameReady(object Sender, ImageEvent e)
    {
        // is the workerthread already processing an image? return.. (skip this image)
        if (_workerThreadIsBusy.WaitOne(0))
            return; // skip frame.
        //create a 'global' ref so the workerthread can access it.
        /* BE CAREFULL HERE. You might experience trouble with the instance of this image. 
         * You are creating another reference to the SAME instance of the image 
         * to process on another thread. When the Camera is reusing this 
         * image (for speed), the image might screwed-up. In that case, 
         * you have to create a copy!         
         * (personally I would reuse the image which makes the image not available outside the event callback) */
        _myImage = e.Image;
        // signal the workerthread, so it starts processing the current image.
        _workerThreadIsBusy.Set();
    }
    private void ImageProcessingThread()
    {
        var waitHandles = new WaitHandle[] { _terminating, _workerThreadIsBusy };
        var run = true;
        while (run)
        {
            switch (EventWaitHandle.WaitAny(waitHandles))
            {
                case 0:
                    // terminating.
                    run = false;
                    break;
                case 1:
                    // process _myImage
                    ProcessImage(_myImage);
                    _workerThreadIsBusy.Reset();
                    break;
            }
        }
        _terminated.Set();
    }
    private void ProcessImage(Image _myImage)
    {
        // whatever...
    }
    // constructor
    public MyCameraProcessor()
    {
        // define the thread.
        _processingThread = new Thread(ImageProcessingThread);
        _processingThread.Start();
    }
    public void Dispose()
    {
        _terminating.Set();
        _terminated.WaitOne();
    }
}

您的代码不是多线程安全的

  if (!IsImageReady) return;  // global var
  IsImageReady = false;
  //... do stuff with the image
  IsImageReady=true;

两个线程可以同时读取IsImageReady,看看它是真的,然后都将其设置为假。如果处理器从缓存而不是从内存读取IsImageReady,也可能会出现问题。Interlocked类可以避免此类问题,它在一个操作中读取和更改值。它还确保缓存不会引起问题。

private int IsImageReady= 0; 
private void Camera_FrameReady(object Sender, ImageEvent e){
  int wasImageReady = Interlocked.Exchange(ref IsImageReady, 1);
  if (wasImageReady ==1) return;
    //do something
    IsImageReady= 0;
  }
}

虽然我不确定这是否是你唯一的问题。你可能还有其他人。可以肯定的是,您必须正确地调试代码,这在涉及多线程时非常困难。阅读我的文章Codeproject:如何实时调试多线程代码。