多线程读取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
相关产品推荐
相关产品推荐

