You need to enable JavaScript to run this app. 多线程读取Bitmap调用LockBits报区域已锁定错误,有什么更优读取方案?
优惠活动
大模型
产品
解决方案
定价
更多

多线程读取Bitmap调用LockBits报区域已锁定错误,有什么更优读取方案?

问题本质

System.Drawing 下的 Bitmap 本身不是线程安全类型,其底层GDI+实现的LockBits方法无论指定ReadOnly还是读写模式,只要某块区域已经被锁定,就不允许其他线程再次发起锁定请求,这是原生框架的固有设计,无法直接绕过。

可行方案(按优先级排序)

1. 加锁串行化锁定操作(最贴合直接使用Bitmap类的需求)

只需用锁包裹LockBits到UnlockBits的完整流程,保证同一时间只有一个线程对位图发起锁定操作,即可避免报错。虽然是串行执行,但内存拷贝操作开销极低,绝大多数场景下性能足够:

// 定义锁对象,和Bitmap实例一一对应
private readonly object _bitmapLock = new object();

// 线程内的读取逻辑
lock (_bitmapLock)
{
    BitmapData bits = videoEntry.Data.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
    var bytes = new byte[width * height * 4];
    Marshal.Copy(bits.Scan0, bytes, 0, bytes.Length);
    videoEntry.Data.UnlockBits(bits);
}

DoSomethingThreadSpecificWithByteArray(bytes, 0, bytes.Length);

2. 单次锁定长期持有(性能最优)

如果该Bitmap在所有线程读取期间不会被修改、销毁,可以提前调用一次LockBits并持有返回的BitmapData对象,所有线程直接读取BitmapData.Scan0指向的内存即可,无需反复加锁解锁,也不需要每个线程单独拷贝字节数组:

  • 初始化阶段(主线程执行,仅执行一次):
// 提前锁定,所有线程读取完成前不要调用UnlockBits
BitmapData sharedBitmapData = videoEntry.Data.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
  • 线程内只读访问逻辑(无锁,可并发执行):
// 直接读取sharedBitmapData.Scan0的内存即可,只读操作多线程并发安全
// 如果需要独立拷贝也可以自己执行Marshal.Copy,不会冲突
DoSomethingThreadSpecificWithIntPtr(sharedBitmapData.Scan0, sharedBitmapData.Stride, width, height);
  • 所有线程处理完成后,再统一调用UnlockBits释放锁定。

3. 内置缓存封装

如果不想手动管理锁或者长期锁定的生命周期,可以自己封装一个线程安全的Bitmap包装类,内部懒加载缓存字节数组,对外暴露读取接口,外部不需要感知缓存逻辑,仍然可以基于Bitmap实例操作。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 15:15:04