You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程WinForms程序‘Object is currently in use elsewhere’问题排查及锁优化

问题解答

一、异常核心信息解析

(a) 执行失败的例程

从异常堆栈跟踪可明确,失败的是**System.Drawing.Image.get_Width()**方法,该方法在WinForms的PictureBox.OnPaint绘制流程中被调用,用于获取图片宽度以计算绘制区域。

(b) 涉及的具体对象

异常直接关联的是绑定到PbWM(你的PictureBox控件)的Image对象。当UI线程触发PictureBox重绘时,尝试读取该Image的Width属性,发现对象正被其他线程占用。

(c) 当前占用对象的代码

虽然日志未直接给出占用线程的代码,但结合你的多线程图片生成逻辑,大概率是某个后台线程正在对该Bitmap对象进行修改、克隆或其他操作(比如你代码中4个阶段的图片生成步骤),而UI线程的绘制操作同时访问了它。

二、为何用了SyncLock仍出现错误

你的现有SyncLock逻辑存在两个关键问题:

  1. 跨线程操作WinForms控件违规:WinForms控件的属性(包括PictureBox.Image)只能在UI线程中修改,你的代码直接在后台线程中设置PbWM.Image,即使加了锁,也违反了WinForms的线程安全规则,可能导致控件内部状态混乱。
  2. 锁的范围和对象选择不当:你锁的是bm对象,但bm可能在其他未加锁的代码段被后台线程操作;同时,当你将bm.Clone赋值给PbWM.Image后,UI线程绘制时访问的是克隆后的Image对象,如果该克隆对象在后台线程中仍被引用并操作,也会引发冲突。

三、System.Transactions出现在日志中的原因

这是.NET框架的系统追踪机制导致的,并非你的代码引用了该组件。.NET会通过System.Transactions模块记录关键的未处理异常追踪信息,属于系统级日志的一部分,和你的业务代码无关。

四、三种锁实现的优劣对比

第一种实现

' The Thread enters with Bitmap bm1..

' I make a working copy of bm1
Using bm0 As Bitmap = CType(bm1.Clone, Bitmap)
    Using gr As Graphics = Graphics.FromImage(bm)
        ' Some image drawing is done here
    End Using

    SyncLock bm
        ' Replace bm1 with the altered image
        bm1 = CType(bm0.Clone, Bitmap)
    End SyncLock
End Using
  • 相对合理,但存在缺陷:锁的是bm这个Bitmap数据对象本身,而Bitmap是可变对象,如果后续bm被重新赋值,新的bm对象会脱离原锁的控制,导致同步失效;同时,如果bm在其他代码段未通过SyncLock bm访问,依然会有并发问题。

第二种实现

' The Thread enters with Bitmap bm1..

' I make a working copy of bm1
Using bm0 As Bitmap = CType(bm1.Clone, Bitmap)
    Using gr As Graphics = Graphics.FromImage(bm)
        ' Some image drawing is done here
    End Using

    Dim thisLock As New Object
    SyncLock thisLock
        ' Replace bm1 with the altered image
        bm1 = CType(bm0.Clone, Bitmap)
    End SyncLock
End Using
  • 完全无效:thisLock是方法内局部变量,每个线程进入都会创建新的锁对象,不同线程的SyncLock无法形成同步,完全起不到线程安全的作用。

第三种实现

' The Thread enters with Bitmap bm1..

' I make a working copy of bm1
Using bm0 As Bitmap = CType(bm1.Clone, Bitmap)
    Using gr As Graphics = Graphics.FromImage(bm)
        ' Some image drawing is done here
    End Using

    Dim thisLock As New Object
    SyncLock thisLock
        ' Replace bm1 with the altered image
        bm1 = New Bitmap(bm0)
    End SyncLock
End Using
  • 同样无效:和第二种问题一致,局部创建的thisLock无法在多线程间形成同步,锁机制完全失效;仅New Bitmap(bm0)比Clone生成的对象更独立(完全复制像素数据),但锁的问题导致同步毫无意义。

最优方案建议

放弃以上三种,采用专用静态锁对象+UI线程更新控件的组合:

  1. 定义全局静态锁对象,避免锁数据对象:
    Private Shared ReadOnly _imageLock As New Object()
    
  2. 后台线程处理图片时,用该锁同步共享的Bitmap对象:
    ' 后台线程代码
    Using bm0 As Bitmap = CType(bm1.Clone, Bitmap)
        Using gr As Graphics = Graphics.FromImage(bm0)
            ' 执行图片绘制操作
        End Using
    
        SyncLock _imageLock
            ' 先释放旧对象
            If bm1 IsNot Nothing Then
                bm1.Dispose()
            End If
            bm1 = New Bitmap(bm0)
        End SyncLock
    
        ' 切换到UI线程更新PictureBox
        PbWM.Invoke(Sub()
                        PbWM.Image?.Dispose()
                        PbWM.Image = CType(bm1.Clone, Bitmap)
                    End Sub)
    End Using
    

内容的提问来源于stack exchange,提问作者Tim Makins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 20:01:12