多线程创建Bitmap调用DrawString时出现“对象正被其他地方使用”错误
问题原因与解决方案
问题出在共享的Font对象上——GDI+的Font类不是线程安全的,多个线程同时调用DrawString时共用同一个Font实例,就会触发“对象正被其他地方使用”的异常,和你每个线程独立的Bitmap、Graphics没关系。
解决步骤:
- 每个线程创建独立的
Font实例,不要共享全局的Font变量。比如在Parallel.ForEach的循环体内,用using声明自己的Font; - 删掉无效的
lock (bitmap)——每个线程的bitmap都是独立实例,锁它根本不会和其他线程产生竞争,完全没用。
修改后的代码示例:
Parallel.ForEach(rectangles, new ParallelOptions() { MaxDegreeOfParallelism = Threads }, rect => { using Bitmap bitmap = new((int)(tileSize.Width * ChunkSize.Width), (int)(tileSize.Height * ChunkSize.Height)); using Graphics graphics = Graphics.FromImage(bitmap); // 每个线程创建独立的Font实例,复制原Font的属性 using Font threadFont = new Font(Font.FontFamily, Font.Size, Font.Style); for (int i = rect.X; i < rect.Right; i++) { for (int j = rect.Y; j < rect.Bottom; j++) { PointF Location = new PointF(tileSize.Width * (i - rect.Width), tileSize.Height * (j - rect.Height)); graphics.FillRectangle(renderTiles[i, j].BackBrush, new Rectangle(Location.ToPoint().Add(new Point(5, -1)), tileSize.ToSize().Add(new Size(1, 1)))); // 使用线程独立的Font graphics.DrawString(renderTiles[i, j].Ico.ToString(), threadFont, renderTiles[i, j].ForeBrush, Location.ToPoint()); } } lock (pe.Graphics) { pe.Graphics.DrawImage(bitmap, rect.Location.Multiply(tileSize.ToPoint())); } });
额外注意:
如果renderTiles里的ForeBrush/BackBrush是共享的可变对象(比如动态修改颜色的Brush),也可能引发线程安全问题。如果是只读的固定Brush(比如固定颜色的SolidBrush),通常没问题;如果是可变的,建议每个线程创建独立的Brush实例。
内容的提问来源于stack exchange,提问作者Doshorte Dovencio
相关产品推荐
相关产品推荐

