<已解决> GDI 多线程绘图方案
- 为什么要用多线程绘制?
绘制的数据量太大,绘制时间过长,如果在主线程绘制,会阻赛主线程,导致UI卡顿
- 多线程绘制的思路
创建多个内存DC,用于线程绘制,主线程定时获取内存DC中的位图,用于刷新UI
- 实现方式,以下以两个线程为例
class CPainter
{
HDC m_hMemDC1 ;
HDC m_hMemDC2 ;
HDC m_hCurDC;
void PaintThreadOne()
{
m_hMemDC1 = CreateCompatibleDC(NULL);
HDC hdcScreen = CreateDC(L"DISPLAY", NULL, NULL, NULL);
HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, 1500, 1500);
::SelectObject(m_hMemDC1, hBitmap);
while (1)
{
// ..... 具体的绘制代码;
m_hCurDC = m_hMemDC1 ;
Sleep(10);
}
}
void PaintThreadTwo()
{
m_hMemDC2 = CreateCompatibleDC(NULL);
HDC hdcScreen = CreateDC(L"DISPLAY", NULL, NULL, NULL);
HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, 1500, 1500);
::SelectObject(m_hMemDC2, hBitmap);
while (1)
{
// ......具体的绘制代码;
m_hCurDC = m_hMemDC2 ;
Sleep(10);
}
}
}
- 主线程实现
class CMainDlg {
CPainter m_painter;
BOOL OnInitDialog ()
{
CDialogEx::OnInitDialog();
SetTimer(NULL, 40, NULL);
return TRUE;
}
void OnTimer ()
{
Invalidate(FALSE);
}
void OnPaint ()
{
CPaintDC dc(this);
BitBlt(dc, 0, 0, 1500, 1500, m_painter->m_hCurDC, 0, 0, SRCCOPY);
}
}
- 以上是基本思路,代码是示意的,有问题请忽略;
- 思路有了,那么接下来就要考虑线程同步的问题了,因为主线程在使用位图1的时候有可能它正在绘制;
我的线程同步没有使用windows提供的方式,因为效率很低;
我采用了两个整型变量:
m_nUsed // 代表正在被主线程使用的
m_nFinished;
- 首先给每个线程编号: m_id1 = 2, m_id2 = 3;
- 给每个线程定义两种状态:绘制完成(用线程自身的id)、正在绘制(线程id的平方) // 别问为什么是平方,实际就是一个数字,平方是我采用的方式,你可以任意选择,只要不和线程id编号冲突就可以;
- 当1线程绘制之前,需要检测m_nUsed 是不是等于自身id(即m_nUsed == 2), 如果是则等待(说明此时主线程在用内存dc1),如果不是则设置m_nFinished 为自己id的平方4,然后绘制,绘制完成设置m_nFinished = 2;线程2 同理;
- 主线程检测m_nFinished 是不是 2 和 3 如果不是则返回, 如果是其中的一个,则修改m_nUsed = m_nFinished; 然后绘制,绘制完成后m_nFinished = 0;
- 同步方式这里插一句:如果你有更好的办法,或者发现我的同步方式有问题,请指点。谢谢!接下来继续上代码!
class CPainter { HDC m_hMemDC1 ; HDC m_hMemDC2 ; HDC m_hCurDC; LONG m_id1 = 2;
LONG m_id2 = 3;
LONG m_nFinished = 0;
LONG m_nUsed
void PaintThreadOne() { m_hMemDC1 = CreateCompatibleDC(NULL); HDC hdcScreen = CreateDC(L"DISPLAY", NULL, NULL, NULL); HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, 1500, 1500); ::SelectObject(m_hMemDC1, hBitmap); while (1) {
while (1)
{
if (m_nUsed != m_id1)
{break;}
Sleep(1);
}
m_nFinished = m_id1 * m_id1; // ..... 具体的绘制代码;
m_nFinished = m_id1; m_hCurDC = m_hMemDC1 ;
Sleep(10); } } void PaintThreadTwo() { m_hMemDC2 = CreateCompatibleDC(NULL); HDC hdcScreen = CreateDC(L"DISPLAY", NULL, NULL, NULL); HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, 1500, 1500); ::SelectObject(m_hMemDC2, hBitmap); while (1) {
while (1)
{
if (m_nUsed != m_id2)
{
break;
}
Sleep(1);
}
m_nFinished = m_id2 * m_id2; // ......具体的绘制代码;
m_nFinished = m_id2;
m_hCurDC = m_hMemDC2 ; Sleep(10); } } }
- 主线程代码
class CMainDlg { CPainter m_painter; BOOL OnInitDialog () { CDialogEx::OnInitDialog(); SetTimer(NULL, 40, NULL); return TRUE; } void OnTimer () { Invalidate(FALSE); } void OnPaint () { CPaintDC dc(this);
if (m_painter->m_nFinished != m_painter->m_id1 && m_painter->m_nFinished != m_id2)
{
}
else
{
BitBlt(dc, 0, 0, 1500, 1500, m_painter->m_hCurDC, 0, 0, SRCCOPY);
}
} }
- 总结
好了,以上是全部代码,你需要把更改m_nUsed和m_nFinished的地方全部改成原子操作;
另外还有一个问题:在主线程定时器触发,但这时m_nFinished中没有可用值时,UI会被刷成没;虽然没解决但经过我测试,基本不影响,后续我会继续解决;

浙公网安备 33010602011771号