【问题标题】:Is a Semaphore the right tool for this video sequence capture/save job?信号量是这个视频序列捕获/保存工作的正确工具吗?
【发布时间】:2010-12-01 20:15:34
【问题描述】:

我正在使用 C# (.NET 4.0) 开发一个 WPF 项目,以从需要保存到磁盘的高速摄像机(BMP 格式)捕获 300 个视频帧序列。视频帧需要以近乎精确的时间间隔捕获,因此我无法在捕获帧时将它们保存到磁盘——磁盘 I/O 是不可预测的,并且它会丢弃帧之间的时间间隔。采集卡有大约 60 个可用的帧缓冲区。

我不确定实施解决此问题的最佳方法是什么。我最初的想法是创建一个“BufferToDisk”线程,在帧缓冲区可用时保存图像。在这种情况下,主线程捕获帧缓冲区,然后向线程发出信号以指示可以保存帧。问题是捕获帧的速度比线程保存文件的速度要快,因此需要某种同步来处理这个问题。我在想信号量将是这项工作的好工具。不过,我从来没有以这种方式使用过信号量,所以我不确定如何继续。

这是解决这个问题的合理方法吗?如果是这样,有人可以发布一些代码让我开始吗?

非常感谢任何帮助。

编辑: 在查看了链接的“C# 中的线程 - 第 2 部分”一书摘录后,我决定通过改编“ProducerConsumerQueue”类示例来实现该解决方案。这是我改编的代码:

class ProducerConsumerQueue : IDisposable
{
    EventWaitHandle _wh = new AutoResetEvent(false);
    Thread _worker;
    readonly object _locker = new object();
    Queue<string> _tasks = new Queue<string>();

    public ProducerConsumerQueue()
    {
        _worker = new Thread(Work);
        _worker.Start();
    }

    public void EnqueueTask(string task)
    {
        lock (_locker) _tasks.Enqueue(task);
        _wh.Set();
    }

    public void Dispose()
    {
        EnqueueTask(null);     // Signal the consumer to exit.
        _worker.Join();         // Wait for the consumer's thread to finish.
        _wh.Close();            // Release any OS resources.
    }

    void Work()
    {
        while (true)
        {
            string task = null;
            lock (_locker)
                if (_tasks.Count > 0)
                {
                    task = _tasks.Dequeue();
                    if (task == null)
                    {
                        return;
                    }
                }
            if (task != null)
            {
                // parse the parameters from the input queue item
                string[] indexVals = task.Split(',');
                int frameNum = Convert.ToInt32(indexVals[0]);
                int fileNum = Convert.ToInt32(indexVals[1]);
                string path = indexVals[2];
                // build the file name
                string newFileName = String.Format("img{0:d3}.bmp", fileNum);
                string fqfn = System.IO.Path.Combine(path, newFileName);
                // save the captured image to disk
                int ret = pxd_saveBmp(1, fqfn, frameNum, 0, 0, -1, -1, 0, 0);
            }
            else
            {
                _wh.WaitOne();         // No more tasks - wait for a signal
            }
        }
    }
}

在主程序中使用类:

// capture bitmap images and save them to disk
using (ProducerConsumerQueue q = new ProducerConsumerQueue())
{
     for (int i = 0; i < 300; i++)
     {
         if (curFrmBuf > numFrmBufs)
         {
              curFrmBuf = 1;  // wrap around to the first frame buffer
         }

         // snap an image to the image buffer
         int ret = pxd_doSnap(1, curFrmBuf, 0);

         // build the parameters for saving the frame to image file (for the queue)
         string fileSaveParams = curFrmBuf + "," + (i + 1) + "," + newPath;
         q.EnqueueTask(fileSaveParams);

         curFrmBuf++;
    }
}

相当漂亮的类——这个功能的少量代码。

非常感谢您的建议,伙计们。

【问题讨论】:

    标签: c# .net wpf video-capture semaphore


    【解决方案1】:

    当然,听起来很合理。您可以使用信号量或其他线程同步原语。这听起来像是一个标准的生产者/消费者问题。看看here 的一些伪代码 .

    【讨论】:

    • 谢谢——它确实看起来像一个生产者/消费者的问题。我会看看它,看看我是否可以基于此制定解决方案。
    【解决方案2】:

    如果磁盘太慢(例如某些其他进程挂起它)以致 60 个帧缓冲区不够用,会发生什么情况?也许您需要BufferToMemoryBufferToDisk 线程或某种组合。您将希望主线程(捕获到缓冲区)具有最高优先级,BufferToMemory 中等,而 BufferToDisk 最低。

    无论如何,回到 Semaphores,我建议您阅读以下内容:http://www.albahari.com/threading/part2.aspx#_Semaphore。信号量应该可以为您解决问题,但我会推荐 SemaphoreSlim (.NET 4)。

    【讨论】:

    • 是的,缓慢的磁盘 I/O 是一个值得关注的问题,但这是一个专用系统,我们的基准测试表明这种情况不太可能发生。
    • 哦,顺便说一句,谢谢你的链接,尼尔森。我已经使用 ProducerConsumerQueue 从该链接实现了一个解决方案,它工作得非常好。我意识到如果卡的视频缓冲区在文件保存之前太早,可能会出现问题,但这在我的应用程序环境中应该不是问题。哦,而且采集卡有75个buffer,不是我原先想的60个,应该没问题。
    【解决方案3】:

    由于您将此视为生产者/消费者问题(从您对 @siz 的回答来看),您可能需要查看专为此类场景设计的 BlockingCollection&lt;T&gt;

    它允许任意数量的生产者线程将数据推送到集合中,并允许任意数量的消费者线程再次将其拉出。在这种情况下,您可能只需要一个生产者和一个消费者线程。

    BlockingCollection&lt;T&gt; 完成所有工作,以确保消费者线程仅在生产线程表示还有更多工作要做时才唤醒并处理工作。它还负责允许建立工作队列。

    【讨论】:

    • 感谢伊恩的建议。我对 ProducerConsumerQueue 解决方案(在原始帖子中作为编辑发布)非常满意,但我会看看 BlockingCollection。 .NET 4 的 System.Collections.Concurrent 命名空间中的好东西肯定会大大减轻并发编程的一些痛苦。姗姗来迟,IMO。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-30
    • 1970-01-01
    • 2017-10-05
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多