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

30

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

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

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

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

Из того, что я могу сказать, эта ошибка происходит из события 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

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

Image DummyImage;

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

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

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

Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededLockBits()Error: User Rate Limit ExceededUnlockBits()Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
0

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
2

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

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

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

Если вы не можете этого сделать, скопируйте полученный кадр с камеры сначала в новый буфер и добавьте этот буфер в очередь, или просто используйте 2 чередующихся буфераand check for overruns, Либо используйте 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;
    }
}
5

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

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

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

Error: User Rate Limit Exceeded

Похожие вопросы