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

MFC中多线程生成HDC后BitBlt至主HDC仅最后结果生效问题

问题分析与解决方案

问题根源

  1. 区域完全覆盖:所有线程执行BitBlt时,都将自身绘制的位图以SRCCOPY模式写入主HDC的(0,0)起始区域,后完成的线程会直接覆盖前面线程的绘制结果,最终仅保留最后一个线程的输出。
  2. 无同步的并发操作:主HDC属于共享GDI资源,多线程无保护直接对其执行BitBlt会引发资源竞争,即使没有覆盖问题也可能导致绘制错乱。
  3. GDI资源泄漏:线程中SelectObject取出的旧位图未恢复到线程HDC,创建的brush也未主动销毁,会造成GDI资源泄漏。

修复方案

1. 解决区域覆盖问题

  • 如果每个Info对应独立的绘制区域:从info中获取专属目标矩形,调整BitBlt的目标坐标,避免全部绘制在同一区域:
    // 示例:从info中获取该线程负责的目标区域
    CRect destRect = info.GetTargetRect();
    ThreadSafeWinGdi::BitBlt(hDC, destRect.left, destRect.top, destRect.Width(), destRect.Height(), threadHdc, 0, 0, SRCCOPY);
    
  • 如果需要图层式叠加绘制:将SRCCOPY替换为SRCPAINT等支持叠加的ROP码,同时给主HDC的BitBlt操作加锁。

2. 主HDC操作的线程同步

在ThreadSafeWinGdi中添加线程安全的BitBlt封装,保证同一时间只有一个线程操作主HDC:

BOOL ThreadSafeWinGdi::BitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, DWORD dwRop)
{
    static std::mutex bitBltMutex;
    std::unique_lock<std::mutex> lock(bitBltMutex);
    return ::BitBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, dwRop);
}

3. 修复GDI资源泄漏

线程绘制完成后,必须恢复SelectObject取出的旧位图,并销毁创建的GDI资源:

// 绘制完成后执行资源清理
threadCDC->SelectObject(oldBitmap); // 恢复线程HDC的旧位图
ThreadSafeWinGdi::DeleteObject(newBitmap);
ThreadSafeWinGdi::DeleteObject(brush.get()); // 销毁创建的画刷

修改后的线程lambda示例

futures.emplace_back(std::async(std::launch::async, [=]()
{
    if (auto dcPtr = ThreadSafeWinGdi::CreateCompatibleDC(hDC))
    {
        HDC threadHdc = dcPtr.get();
        CDC* threadCDC = ThreadSafeWinGdi::FromHandle(threadHdc);
        auto view = static_cast<View*>(App::GetView());

        CRect rect = view->GetViewRect();
        HBITMAP newBitmap = ThreadSafeWinGdi::CreateCompatibleBitmap(hDC, rect.Width(), rect.Height());
        auto oldBitmap = (HBITMAP)(threadCDC->SelectObject(newBitmap));
        auto brush = ThreadSafeWinGdi::CreateSolidBrush(view->GetBkColor(_Env().GetEllipsoid()));
        ThreadSafeWinGdi::FillRect(threadHdc, rect, brush.get());

        CManager manager;
        manager.SetEventWnd(coconutView);
        manager.SetDC(_iPane, threadHdc);
        manager.SetViewRect(_iPane, scrRect);
        manager.SetMapRect(_iPane, App::GetERManager().GetDrawMapRect(_iPane), FALSE);

        DrawInfo(manager, _iPane, info);

        // 同步叠加绘制到主HDC
        ThreadSafeWinGdi::BitBlt(hDC, 0, 0, rect.Width(), rect.Height(), threadHdc, 0, 0, SRCPAINT);
        
        // 清理GDI资源
        threadCDC->SelectObject(oldBitmap);
        ThreadSafeWinGdi::DeleteObject(newBitmap);
        ThreadSafeWinGdi::DeleteObject(brush.get());
    }
}));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:33:28