C#开发国际象棋棋盘识别工具时GetPixel()卡顿疑似异步问题求助
问题根因
Bitmap对象提前释放
你在getPrintScreenForSquare方法中用using包裹了创建的Bitmap对象,离开using作用域时Bitmap会被自动销毁,你返回给调用方的是已经释放的无效GDI对象,调用GetPixel访问无效资源时就会出现挂起、无响应的现象,且这类GDI操作异常很容易被异步上下文吞掉,不会主动抛出。async/await误用
你的FindWhatIsInThisSquareAsync方法没有任何异步IO操作,全是CPU密集型逻辑,加了async关键字却没有await语句,不仅会产生编译器警告,还会导致异常被包装到Task中难以排查,手动创建64个Task跑同步代码的写法也完全没有必要,效率远低于标准的并行处理方案。亮度判断逻辑错误
Color.GetBrightness()的返回值范围是0.0(纯黑)~1.0(纯白)的浮点数,你定义的阈值(90、50等)远远超过合法范围,就算前面的问题解决,判断条件也永远不会符合预期。
修复方案
1. 修复Bitmap释放问题
去掉getPrintScreenForSquare中Bitmap外层的using,改为调用方用完后释放:
private static Bitmap getPrintScreenForSquare(int i, int j) { Rectangle bounds = new Rectangle(308 + i * 99, 170 + j * 99, 95, 95); // 去掉外层using,不要在这里销毁Bitmap Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height); using (Graphics g = Graphics.FromImage(bitmap)) { g.CopyFromScreen(new Point(bounds.X, bounds.Y), Point.Empty, bounds.Size); } return bitmap; }
2. 移除无效async,改用并行处理
CPU密集型的棋盘扫描用Parallel更合适,不需要强行用异步:
// 改为同步方法,用完Bitmap后主动释放 private static void FindWhatIsInThisSquare(int i, int j) { using (Bitmap square = getPrintScreenForSquare(i, j)) { bool isSquareWhite = (i + j) % 2 == 0; if (IsSquareEmpty(square, isSquareWhite)) { Bord[i][j] = ' '; } else { Bord[i][j] = GetValueForPiece(); } } } // 用Parallel代替手动创建Task列表 private static void FillBoard() { Parallel.For(0, Bord.Length, i => { for (int j = 0; j < Bord[i].Length; j++) { FindWhatIsInThisSquare(i, j); } }); } // 异步入口改为用Task.Run包装同步并行逻辑 public static Task<char[][]> GetBoardAsync() { return Task.Run(() => { GetEmptyBoard(); FillBoard(); return Bord; }); }
3. 修复亮度判断逻辑
二选一即可:
// 方案1:调整阈值为0~1的浮点数 public static float minBrightnessOfWhiteSquare = 0.9f; public static float maxBrightnessOfBlackSquare = 0.5f; // 方案2:用RGB计算0~255范围的亮度,和原有阈值匹配(精度更高更推荐) private static float GetLuminance(Color c) { return 0.299f * c.R + 0.587f * c.G + 0.114f * c.B; } // 把IsSquareEmpty中的GetBrightness替换为GetLuminance即可
额外注意事项
如果坐标是硬编码的,建议先输出截图验证棋盘区域是否正确,避免坐标偏移导致读取到棋盘外的内容。
内容的提问来源于stack exchange,提问作者Stef
相关产品推荐
相关产品推荐

