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

时间:2015-11-13 13:12:07

标签: c# multithreading

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

我更改了程序以确保图像流永远不会排队 通过简单地检查布尔值来检查当前帧是否未被处理。通常这不会发生,但在某些情况下确实如此。 例如,当在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时它是否有效,它具有"世界观"当前的全球价值观?或者全局变量仅在处理事件后更新?

2 个答案:

答案 0 :(得分:1)

我头脑中的第一件事是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();
    }
}

答案 1 :(得分:0)

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

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

2个线程可以同时读取IsImageReady,看到它是真的并且都将它设置为false。如果处理器从缓存而不是从内存读取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: Debugging multithreaded code in real time,了解如何做到这一点。