Подтвердить что ты не робот

InvalidOperationException - объект в настоящее время используется в другом месте - красный крест

У меня есть настольное приложение С#, в котором один поток, который я создаю, постоянно получает изображение из источника (фактически это цифровая камера) и помещает его в панель (panel.Image = img) в графическом интерфейсе (который должен быть другой поток, поскольку он является кодом для элемента управления.

Приложение работает, но на некоторых машинах я получаю следующую ошибку в случайных временных интервалах (непредсказуемый)

************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere. 

Затем панель превращается в красный крест, красный X - я думаю, что это недопустимый значок изображения, который можно редактировать из свойств. Приложение продолжает работать, но панель никогда не обновляется.

Из того, что я могу сказать, эта ошибка возникает из события onpaint управления, где я рисую что-то еще на картинке.

Я попытался использовать блокировку там, но не повезло: (

То, как я вызываю функцию, которая помещает изображение на панель, выглядит следующим образом:

if (this.ReceivedFrame != null)
{
    Delegate[] clients = this.ReceivedFrame.GetInvocationList();
    foreach (Delegate del in clients)
    {
        try
        {
            del.DynamicInvoke(new object[] { this, 
                new StreamEventArgs(frame)} );
        }
        catch { }
    }
}

это делегат:

public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e);
    public event ReceivedFrameEventHandler ReceivedFrame;

и именно так в нем записывается функция внутри управляющего кода:

Camera.ReceivedFrame += 
    new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame);

Я также пробовал

del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) });

вместо

del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) });

но не повезло

Кто-нибудь знает, как я могу исправить эту ошибку или, по крайней мере, поймать ошибку так или иначе, чтобы поток снова помещал изображения на панель?

4b9b3361

Ответ 1

Это связано с тем, что класс Gdi + Image не является потокобезопасным. Hovewer вы можете избежать InvalidOperationException, используя блокировку каждый раз, когда вам нужен доступ к изображениям, например, для рисования или получения размера изображения:

Image DummyImage;

// Paint
lock (DummyImage)
    e.Graphics.DrawImage(DummyImage, 10, 10);

// Access Image properties
Size ImageSize;
lock (DummyImage)
    ImageSize = DummyImage.Size;

Кстати, вызов не нужен, если вы будете использовать приведенный выше шаблон.

Ответ 2

У меня была аналогичная проблема с тем же сообщением об ошибке, но, как я мог, попытка блокировки растрового изображения ничего не исправила для меня. Затем я понял, что рисую фигуру с помощью статической кисти. Разумеется, именно кисть вызывала конфликт потоков.

var location = new Rectangle(100, 100, 500, 500);
var brush = MyClass.RED_BRUSH;
lock(brush)
    e.Graphics.FillRectangle(brush, location);

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

Ответ 3

Мне кажется, что один и тот же объект камеры используется несколько раз.

например. попробуйте использовать новый буфер для каждого полученного кадра. Мне кажется, что в то время как поле с рисунком рисует новый кадр, ваша библиотека захвата снова заполняет этот буфер. Поэтому на более быстрых машинах это может быть не проблема, а медленные машины могут быть проблемой.

Я запрограммировал что-то подобное один раз, после каждого принятого фрейма нам пришлось запросить, чтобы получить следующий фрейм и установить буфера приема NEW в этом запросе.

Если вы не можете этого сделать, скопируйте полученный кадр из камеры сначала в новый буфер и добавьте этот буфер в очередь или просто используйте 2 переменных буфера и проверьте перерасход. Либо используйте myOutPutPanel.BeginInvoke для вызова метода camera_ReceivedFrame, либо лучше иметь поток, который проверяет очередь, когда у нее есть новая запись, которую он вызывает mnyOutPutPanel.BeginInvoke, чтобы вызвать ваш метод для установки нового буфера как изображения на панели.

Кроме того, после того, как вы получили буфер, используйте метод вызова панели, чтобы вызвать настройку изображения (убедитесь, что он работает в потоке окна, а не в потоке из вашей библиотеки захвата).

Нижеприведенный пример можно вызвать из любого потока (библиотеки захвата или другого отдельного потока):

void camera_ReceivedFrame(object sender, StreamEventArgs e)
{
    if(myOutputPanel.InvokeRequired)
    {
        myOutPutPanel.BeginInvoke( 
            new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame), 
            sender, 
            e);
    }
    else
    {
        myOutPutPanel.Image = e.Image;
    }
}

Ответ 4

Я думаю, что это проблема многопоточности Используйте золотое правило Windows и обновите панель на панели управления основным потоком. Invoke Это должно преодолеть исключение перекрестной резьбы