InvalidOperationException - 对象当前正在其他地方使用 - 红叉

30 c# multithreading gdi+ invalidoperationexception winforms

我有一个C#桌面应用程序,其中我创建的一个线程不断从源(实际上是数码相机)获取图像并将其放在GUI中的面板(panel.Image = img)上(必须是另一个线程)它是控件的代码隐藏.

应用程序工作,但在某些机器上,我随机的时间间隔得到以下错误(不可预测)

************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere. 
Run Code Online (Sandbox Code Playgroud)

然后面板变成红十字,红色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 { }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是代表:

public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e);
    public event ReceivedFrameEventHandler ReceivedFrame;
Run Code Online (Sandbox Code Playgroud)

这就是控制代码隐藏中的函数如何注册到它:

Camera.ReceivedFrame += 
    new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame);
Run Code Online (Sandbox Code Playgroud)

我也试过了

del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) });
Run Code Online (Sandbox Code Playgroud)

代替

del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) });
Run Code Online (Sandbox Code Playgroud)

但没有运气

有谁知道我怎么能修复这个错误或至少以某种方式捕获错误并使线程再次将图像放在面板上?

arb*_*ter 20

这是因为Gdi + Image类不是线程安全的.Hovewer你可以在每次需要Image访问时使用锁来避免InvalidOperationException,例如绘画或获取图像大小:

Image DummyImage;

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

// Access Image properties
Size ImageSize;
lock (DummyImage)
    ImageSize = DummyImage.Size;
Run Code Online (Sandbox Code Playgroud)

顺便说一句,如果您将使用上述模式,则不需要调用.


Nic*_*tch 5

我遇到了类似的问题,同样的错误信息,但我尽可能地尝试,锁定位图没有为我解决任何问题.然后我意识到我正在使用静态画笔绘制一个形状.果然,正是导致线程争用的刷子.

var location = new Rectangle(100, 100, 500, 500);
var brush = MyClass.RED_BRUSH;
lock(brush)
    e.Graphics.FillRectangle(brush, location);
Run Code Online (Sandbox Code Playgroud)

这适用于我的案例和经验教训:检查在发生线程争用时使用的所有引用类型.


Ahm*_*aid 0

我认为这是多线程问题使用windows黄金法则并在主线程中更新面板使用panel.Invoke这应该克服跨线程异常