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

C#开发国际象棋棋盘识别工具时GetPixel()卡顿疑似异步问题求助

问题根因

  1. Bitmap对象提前释放
    你在getPrintScreenForSquare方法中用using包裹了创建的Bitmap对象,离开using作用域时Bitmap会被自动销毁,你返回给调用方的是已经释放的无效GDI对象,调用GetPixel访问无效资源时就会出现挂起、无响应的现象,且这类GDI操作异常很容易被异步上下文吞掉,不会主动抛出。

  2. async/await误用
    你的FindWhatIsInThisSquareAsync方法没有任何异步IO操作,全是CPU密集型逻辑,加了async关键字却没有await语句,不仅会产生编译器警告,还会导致异常被包装到Task中难以排查,手动创建64个Task跑同步代码的写法也完全没有必要,效率远低于标准的并行处理方案。

  3. 亮度判断逻辑错误
    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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 16:45:03