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

У меня есть настольное приложение C #, в котором один созданный мной поток непрерывно получает изображение из источника (на самом деле это цифровая камера) и помещает его на панель (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) });

но не повезло

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

Ответы [ 4 ]

20 голосов
/ 30 июня 2009

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

Image DummyImage;

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

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

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

5 голосов
/ 06 января 2013

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

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

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

2 голосов
/ 18 марта 2012

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

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

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

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

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

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

void camera_ReceivedFrame(object sender, StreamEventArgs e)
{
    if(myOutputPanel.InvokeRequired)
    {
        myOutPutPanel.BeginInvoke( 
            new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame), 
            sender, 
            e);
    }
    else
    {
        myOutPutPanel.Image = e.Image;
    }
}
0 голосов
/ 30 июня 2009

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

...