<已解决> 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;

  1. 首先给每个线程编号: m_id1 = 2, m_id2 = 3;
  2. 给每个线程定义两种状态:绘制完成(用线程自身的id)、正在绘制(线程id的平方) // 别问为什么是平方,实际就是一个数字,平方是我采用的方式,你可以任意选择,只要不和线程id编号冲突就可以;
  3. 当1线程绘制之前,需要检测m_nUsed 是不是等于自身id(即m_nUsed == 2), 如果是则等待(说明此时主线程在用内存dc1),如果不是则设置m_nFinished 为自己id的平方4,然后绘制,绘制完成设置m_nFinished = 2;线程2 同理;
  4. 主线程检测m_nFinished 是不是 2 和 3 如果不是则返回, 如果是其中的一个,则修改m_nUsed = m_nFinished; 然后绘制,绘制完成后m_nFinished = 0;
  5. 同步方式这里插一句:如果你有更好的办法,或者发现我的同步方式有问题,请指点。谢谢!接下来继续上代码!

 

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会被刷成没;虽然没解决但经过我测试,基本不影响,后续我会继续解决;

 

posted @ 2019-01-13 10:39  jackieron  阅读(1160)  评论(0)    收藏  举报