C++ 之win32多线程应用总结

InterlockedIncrement

函数的作用:

在多线程同时对一个变量访问时,保证一个线程访问变量时其他线程不能访问

  • 事件是很常用的多线程同步互斥机制
HANDLE CreateEvent(

 LPSECURITY_ATTRIBUTES lpEventAttributes, // SECURITY_ATTRIBUTES结构指针,可为NULL
 BOOL bManualReset,     // 手动/自动
                                     // TRUE:表示手动,在WaitForSingleObject后必须手动调用ResetEvent清除信号
                                    // FALSE:表示自动,在WaitForSingleObject后,系统自动清除事件信号
 BOOL bInitialState,        //初始状态,FALSE为无信号,TRUE为有信号
 LPCTSTR lpName         //事件的名称
    );

下边是使用演示:
在这里插入图片描述

在这里插入图片描述

CreateThread

在此将写一个简单的多线程程序,用以展示多线程的功能和使用方法。该程序的主要的思想是画3个进度条,分别以多线程和单线程方式完成,大家可以比较一下。

说明:

(1)该程序还将和单线程做对比。

(2)由于给线程的函数传递了多个参数,所以采用结构体的方式传递参数。

(3)为了演示效果,采用了比较耗时的打点处理。

//线程函数声明
DWORD WINAPI ThreadProc(LPVOIDlpParam);
//为了传递多个参数,我采用结构体
struct threadInfo
{
    
    
    HWND hWnd;       //窗口句柄
    int  nOffset;    //偏移量
    COLORREF clrRGB; //颜色
};
 
protected:
HANDLE hThead[3];    //用于存储线程句柄
    DWORD  dwThreadID[3];//用于存储线程的ID
  threadInfo Info[3];   //传递给线程处理函数的参数
//----> 代码实现

//单线程测试
void CMultiThread_1Dlg::OnBnClickedButton1()
{
    
    
    // TODO: 在此添加控件通知处理程序代码
    //使能按钮
    GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);
    GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE);
    CDC *dc = GetDC();
    CRect rt;
    GetClientRect(rt);
    dc->FillSolidRect(0,0,rt.Width(),rt.Height()-70,RGB(240,240,240));//刷新背景
    dc->TextOut(97,470,"#1");
    dc->TextOut(297,470,"#2");
    dc->TextOut(497,470,"#3");
    //#1
    for (int i=0;i<460;i++)
    {
    
    
       for (int j =10 ;j<200;j++)
       {
    
    
           dc->SetPixel(j,460-i,RGB(255,0,0));
       }
    }
    //#2
    for (int i=0;i<460;i++)
    {
    
    
       for (int j =210 ;j<400;j++)
        {
    
    
           dc->SetPixel(j,460-i,RGB(0,255,0));
       }
    }
    //#3
    for (int i=0;i<460;i++)
    {
    
    
       for (int j =410 ;j<600;j++)
       {
    
    
           dc->SetPixel(j,460-i,RGB(0,0,255));
       }
    }
    ReleaseDC(dc);
    //使能按钮
    GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);
    GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE);
}
 
//多线程测试
void CMultiThread_1Dlg::OnBnClickedButton2()
{
    
    
    // TODO: 在此添加控件通知处理程序代码
    CDC *dc = GetDC();
    CRect rt;
    GetClientRect(rt);
    dc->FillSolidRect(0,0,rt.Width(),rt.Height()-70,RGB(240,240,240));//刷新背景
    dc->TextOut(97,470,"#1");
    dc->TextOut(297,470,"#2");
    dc->TextOut(497,470,"#3");
    //初始化线程的参数
    Info[0].hWnd = Info[1].hWnd = Info[2].hWnd = GetSafeHwnd();
    Info[0].nOffset = 10;Info[1].nOffset = 210;Info[2].nOffset = 410;
    Info[0].clrRGB = RGB(255,0,0);Info[1].clrRGB= RGB(0,255,0);Info[2].clrRGB = RGB(0,0,255);
    //创建线程
    for (int i = 0;i<3;i++)
    {
    
    
       hThead[i] = CreateThread(NULL,0,ThreadProc,&Info[i],0,&dwThreadID[i]);
    }
    ReleaseDC(dc);
}
 
DWORD WINAPI ThreadProc(LPVOIDlpParam)
{
    
    
    threadInfo*Info = (threadInfo*)lpParam;
    CDC *dc = CWnd::FromHandle(Info->hWnd)->GetDC();
    for (int i=0;i<460;i++)
    {
    
    
       for (int j=Info->nOffset;j<Info->nOffset+190;j++)
       {
    
    
           dc->SetPixel(j,460-i,Info->clrRGB);
       }
    }
    DeleteObject(dc);
    return 0;
}

测试效果如下:
在这里插入图片描述
在这里插入图片描述

GetMessage

  • GetMessage是从调用线程的消息队列里取得一个消息并将其放于指定的结构。此函数可取得与指定窗口联系的消息和由PostThreadMessage寄送的线程消息。此函数接收一定范围的消息值。GetMessage不接收属于其他线程或应用程序的消息。获取消息成功后,线程将从消息队列中删除该消息。函数会一直等待直到有消息到来才有返回值。
  • GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax)
参数:
lpMsg:指向MSG结构的指针,该结构从线程的消息队列里接收消息信息。
hWnd:取得其消息的窗口的句柄。当其值取NULL时,GetMessage为任何属于调用线程的窗口检索消息,
      线程消息通过PostThreadMessage寄送给调用线程。
wMsgFilterMin:指定被检索的最小消息值的整数。
wMsgFilterMax:指定被检索的最大消息值的整数。
返回值:如果函数取得WM_QUIT之外的其他消息,返回非零值。如果函数取得WM_QUIT消息,返回值是零。
     如果出现了错误,返回值是-1。例如,当hWnd是无效的窗口句柄或lpMsg是无效的指针时。
     若想获得更多   的错误信息,请调用GetLastError函数。

PeekMessage

  • PeekMessage 调用的一个例子:
PeekMessage(&msg,NULL,0,0,PM_REMOVE);

前面的4个参数(一个指向 MSG 结构的指针、一个窗口的句柄、两个值指示消息范围)与 GetMessage 的参数相同。
将第二、三、四个参数设置为 NULL 或 0时,表明我们想让 PeekMessage 返回程序中所有窗口的所有消息。

如果要将消息从消息队列中删除,则将 PeekMessage 的最后一个参数设置为 PM_REMOVE。
如果不希望删除消息,则将最后一个参数设置为 PM_NOREMOVE,这使得程序可以检查程序的消息队列中的下一个消息,而不实际删除它。

GetMessage 不将控制返回给程序,直到从程序的消息队列中获取消息,但是 PeekMessage 总是立刻返回,而不论一个消息是否出现。
当(应用程序的)消息队列中有一个消息时,PeekMessage 的返回值为 TRUE(非0),并且将按通常方式处理消息。当队列中没有消息时,PeekMessage 返回 FALSE(0)。
考虑如下例子:

// 普通的消息循环 
while( GetMessage(&msg,NULL,0,0) )
{
    
    
  TranslateMessage(&msg);
  DispatchMessage (&msg);
}
return msg.wParam;

// 等价于
while( TRUE )
{
    
    
  if( PeekMessage(&msg,NULL,0,0,PM_REMOVE) )
  {
    
    
    if(msg.message == WM_QUIT)
    {
    
    
      break;
    }
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  else
  {
    
    
    // Other program lines to do some work
  }
}
return msg.wParam;

如果 PeekMessage 的返回值为 TRUE,则消息按通常方式进行处理。如果返回值为 FALSE,则在将控制返回给Windows(操作系统) 之前,还可以做一点工作(如显示另一个随机矩形)。

PeekMessage 不能从消息队列中删除 WM_PAINT 消息。
从队列中删除 WM_PAINT 消息的唯一方法是令窗口客户区的失效区域变得有效,这可以用 ValidateRect 和 ValidateRgn 或者 BeginPaint 和 EndPaint 对来完成。

不能使用如下所示的代码来清除消息队列中的所有消息:
while( PeekMessage(&msg,NULL,0,0,PM_REMOVE) );
这条语句从消息队列中删除 WM_PAINT 之外的所有消息。如果队列中有一个 WM_PAINT 消息,程序就会永远地陷在 while 循环中。

WaitForSingleObject

  • 等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止
DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);

当线程调用该函数时,第一个参数hObject标识一个能够支持被通知/未通知的内核对象。第二个参数dwMilliseconds.允许该线程指明,为了等待该对象变为已通知状态,它将等待多长时间。

  • 调用下面这个函数将告诉系统,调用函数准备等待到hProcess句柄标识的进程终止运行为止:
WaitForSingleObject(hProcess, INFINITE);

第二个参数告诉系统,调用线程愿意永远等待下去(无限时间量),直到该进程终止运行。
通常情况下, INFINITE是作为第二个参数传递给WaitForSingleObject的,不过也可以传递任何一个值(以毫秒计算)。顺便说一下, INFINITE已经定义为0xFFFFFFFF(或-1)。当然,传递INFINITE有些危险。如果对象永远不变为已通知状态,那么调用线程永远不会被唤醒,它将永远处于死锁状态,
不过,它不会浪费宝贵的CPU时间。

  • 下面是如何用一个超时值而不是INFINITE来调用WaitForSingleObject的例子:
DWORD dw = WaitForSingleObject(hProcess, 5000);
switch(dw)
{
    
    
   case WAIT_OBJECT_0:
      // The process terminated.
      break;
   case WAIT_TIMEOUT:
      // The process did not terminate within 5000 milliseconds.
      break;
   case WAIT_FAILED:
      // Bad call to function (invalid handle?)
      break;
}

上面这个代码告诉系统,在特定的进程终止运行之前,或者在5 0 0 0 m s时间结束之前,调用线程不应该变为可调度状态。因此,如果进程终止运行,那么这个
函数调用将在不到5000ms的时间内返回,如果进程尚未终止运行,那么它在大约5000ms时间内返回。注意,不能为dwMilliseconds传递0。如果传递了0,WaitForSingleObject函数将总是立即返回。WaitForSingleObject的返回值能够指明调用线程为什么再次变为可调度状态。如果线程等待的对象变为已通知状态,那么返回值是WAIT_OBJECT_0。如果设置的超时已经到期,则返回值是WAIT_TIMEOUT。如果将一个错误的值(如一个无效句柄)传递给WaitForSingleObject,那么返回值将是WAIT_FAILED(若要了解详细信息,可调用GetLastError)。

猜你喜欢

转载自blog.csdn.net/yohnyang/article/details/134592465