BlockingCollection, состояние гонки?

Я реализовал шаблон Producer/Consumer, используя BlockingCollection, однако он, похоже, не блокируется, как я ожидаю.

У меня есть один поток, получающий кадры с веб-камеры и добавляющий их в BlockingCollection.

private void video_NewFrame(object sender, NewFrameEventArgs eventArgs) {
    image = (Bitmap)eventArgs.Frame.Clone();
    queue.Add(image);
    if (NewFrame != null)
        NewFrame(this, new NewFrameEventArgs(image)); //invoke the event for display
}

И в другом потоке у меня есть ссылка на коллекцию и обработка кадров с помощью

public void Run() {
    foreach (Bitmap bmp in queue.GetConsumingEnumerable()) {
        // process bitmap

Однако, как вы можете видеть ниже, он имеет тенденцию вызывать исключение InvalidOperationException, говорящее мне, что кадр, который я извлекаю, используется где-то еще.

http://i17.photobucket.com/albums/b52/orubap/2012-03-24_020858.png

Это не всегда происходит сразу, но я заметил, что это происходит только тогда, когда очередь пуста или почти пуста (т.е. потребитель быстрее, чем производитель), поэтому я предполагаю, что это как-то связано с первым добавленным изображением или последнее сделанное изображение. Любые идеи, почему это может происходить?


person hspim    schedule 24.03.2012    source источник
comment
Похоже, вы используете изображение в двух местах. Один находится в потребителе коллекции, другой — в обработчике событий. Это, скорее всего, ваша проблема. BlockingCollection ничего не знает о других вещах, которыми вы можете заниматься, и это вам не поможет.   -  person svick    schedule 24.03.2012


Ответы (1)


Поток, который выполняет video_NewFrame, использует изображение, когда оно передается обработчику событий NewFrame. Поскольку это выполняется одновременно с Run, ничто не мешает двум потокам получить доступ к image одновременно. (Это произойдет только тогда, когда Run удаляет изображение из очереди, пока обработчик событий NewFrame обрабатывает его, что объясняет, почему вы видите его только тогда, когда очередь пуста или почти пуста.)

Одним из решений может быть перемещение вызова на NewFrame перед queue.Add(image);video_NewFrame). Это гарантирует, что Run не увидит его, пока обработчик события не завершит работу с ним (при условии, что обработчик события не хранит ссылку на него).

person Bradley Grainger    schedule 24.03.2012
comment
Вы и svick правы, проблема в обработчике событий. Я думал, что NewFrameEventArgs запускает копию, а не ссылку на своих подписчиков. Независимо от того, добавил ли я в очередь или сначала запустил NewFrame, решение заключалось в том, чтобы убедиться, что я использую клон, чтобы вторая операция не использовала ту же ссылку. Спасибо! - person hspim; 24.03.2012