Является ли семафор подходящим инструментом для захвата / сохранения видеопоследовательности? - PullRequest
0 голосов
/ 01 декабря 2010

Я работаю над проектом WPF с C # (.NET 4.0) для захвата последовательности из 300 видеокадров с высокоскоростной камеры, которую необходимо сохранить на диск (формат BMP). Видеокадры должны быть записаны с почти точными временными интервалами, поэтому я не могу сохранить кадры на диск по мере их захвата - дисковый ввод-вывод непредсказуем и отбрасывает временные интервалы между кадрами. Карта захвата имеет около 60 кадровых буферов.

Я не уверен, что лучший подход для реализации решения этой проблемы. Сначала я хочу создать поток «BufferToDisk», который сохраняет изображения из кадровых буферов по мере их появления. В этом сценарии основной поток захватывает буфер кадра и затем сигнализирует потоку, чтобы указать, что все в порядке, чтобы сохранить кадр. Проблема состоит в том, что кадры захватываются быстрее, чем поток может сохранить файлы, поэтому для их решения необходима какая-то синхронизация. Я думал, что семафор будет хорошим инструментом для этой работы. Я никогда не использовал Семафор таким образом, поэтому я не уверен, что делать дальше.

Это разумный подход к этой проблеме? Если да, может кто-нибудь выложить какой-нибудь код, чтобы я начал?

Любая помощь очень ценится.

Edit: Просматривая связанный отрывок книги «Threading in C # - Part 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++;
    }
}

Довольно гладкий класс - небольшое количество кода для этой функции.

Большое спасибо за предложения, ребята.

Ответы [ 3 ]

1 голос
/ 01 декабря 2010

Что произойдет, если диск настолько медленный (например, какой-то другой процесс привязывает его), что 60 кадровых буферов недостаточно? Возможно, вам понадобится нить BufferToMemory и BufferToDisk или какая-то комбинация. Вы хотите, чтобы основной поток (захват в буфер) имел наивысший приоритет, носитель BufferToMemory и BufferToDisk наименьший.

В любом случае, возвращаясь к семафорам, я рекомендую вам прочитать это: http://www.albahari.com/threading/part2.aspx#_Semaphore. Семафоры должны помочь вам, хотя я бы порекомендовал SemaphoreSlim (.NET 4).

1 голос
/ 02 декабря 2010

Поскольку вы рассматриваете это как проблему производителя / потребителя (судя по вашему ответу на ответ @ siz), вы можете захотеть взглянуть на BlockingCollection<T>, который предназначен именно для такого сценария.

Это позволяет любому количеству потоков производителей помещать данные в коллекцию, а любому количеству потоков потребителей - извлекать их снова. В этом случае вам, вероятно, нужен только один производитель и один потребительский поток.

BlockingCollection<T> выполняет всю работу по обеспечению того, чтобы потребительский поток только просыпался и обрабатывал работу, как только производящий поток сказал, что есть еще работа. И это также заботится о создании очереди работы.

1 голос
/ 01 декабря 2010

Конечно, звучит разумно. Вы можете использовать семафоры или другие примитивы синхронизации потоков. Это звучит как стандартная проблема производителя / потребителя. Взгляните здесь для некоторого псевдокода .

...