Summary of win32 multi-threaded applications in C++

InterlockedIncrement

What the function does:

When multiple threads access a variable at the same time, it is guaranteed that when one thread accesses the variable, other threads cannot access it.

  • Events are a very commonly used multi-thread synchronization mutual exclusion mechanism.
HANDLE CreateEvent(

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

Below is a usage demonstration:
Insert image description here

Insert image description here

CreateThread

Here we will write a simple multi-threaded program to demonstrate the functions and usage of multi-threading. The main idea of ​​this program is to draw three progress bars, which are completed in multi-threaded and single-threaded ways. You can compare them.

illustrate:

(1) This program will also be compared with single thread.

(2) Since multiple parameters are passed to the thread function, the parameters are passed in the form of a structure.

(3) In order to demonstrate the effect, a relatively time-consuming dotting process is used.

//线程函数声明
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;
}

The test results are as follows:
Insert image description here
Insert image description here

GetMessage

  • GetMessage obtains a message from the calling thread's message queue and places it in the specified structure. This function can obtain the message associated with the specified window and the thread message sent by PostThreadMessage. This function receives a range of message values. GetMessage does not receive messages belonging to other threads or applications. After successfully obtaining the message, the thread will delete the message from the message queue. The function will wait until a message arrives before returning a value.
  • 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

  • An example of a PeekMessage call:
PeekMessage(&msg,NULL,0,0,PM_REMOVE);

The four parameters before (a pointer to the MSG structure, a window handle, and two values ​​indicating the message range) are the same as the parameters of GetMessage.
When setting the second, third, and fourth parameters to NULL or 0, it indicates that we want PeekMessage to return all messages for all windows in the program.

If you want to remove the message from the message queue, set the last parameter of PeekMessage to PM_REMOVE.
If you do not want the message to be deleted, set the last parameter to PM_NOREMOVE, which allows the program to check for the next message in the program's message queue without actually deleting it.

GetMessage does not return control to the program until a message is obtained from the program's message queue, but PeekMessage always returns immediately, regardless of whether a message appears.
When there is a message in the message queue (the application's), the return value of PeekMessage is TRUE (non-zero), and the message is processed as usual. PeekMessage returns FALSE(0) when there are no messages in the queue.
Consider the following example:

// 普通的消息循环 
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;

If the return value of PeekMessage is TRUE, the message is processed in the usual way. If the return value is FALSE, you can do a little work (such as displaying another random rectangle) before returning control to Windows (operating system).

PeekMessage cannot delete the WM_PAINT message from the message queue.
The only way to remove a WM_PAINT message from the queue is to make the invalid area of ​​the window client area valid, which can be done using the ValidateRect and ValidateRgn or BeginPaint and EndPaint pairs.

You cannot use the following code to clear all messages in the message queue:
while( PeekMessage(&msg,NULL,0,0,PM_REMOVE) ); a>
This statement deletes all messages except WM_PAINT from the message queue. If there is a WM_PAINT message in the queue, the program will be stuck in the while loop forever.

WaitForSingleObject

  • The wait function allows a thread to voluntarily enter a waiting state until a specific kernel object becomes notified.
DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);

When a thread calls this function, the first parameter hObject identifies a kernel object that can support notification/unnotification. The second parameter, dwMilliseconds., allows the thread to specify how long it will wait in order to wait for the object to become notified.

  • Calling the following function will tell the system that the calling function is ready to wait until the process identified by the hProcess handle terminates:
WaitForSingleObject(hProcess, INFINITE);

The second parameter tells the system that the calling thread is willing to wait forever (an unlimited amount of time) until the process terminates.
Normally, INFINITE is passed to WaitForSingleObject as the second parameter, but any value (in milliseconds) can also be passed. By the way, INFINITE is already defined as 0xFFFFFFFF (or -1). Of course, passing INFINITE is somewhat dangerous. If the object never changes to the notified state, then the calling thread will never be awakened and it will be in a deadlock state forever,
However, it will not waste precious CPU time.

  • Here is an example of how to call WaitForSingleObject with a timeout value instead of INFINITE:
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;
}

The above code tells the system that the calling thread should not become schedulable before the specific process terminates or before the end of 5000 ms. Therefore, if the process terminates, this
function call will return in less than 5000ms, and if the process has not terminated, it will return in approximately 5000ms. Note that you cannot pass 0 for dwMilliseconds. If 0 is passed, the WaitForSingleObject function will always return immediately. The return value of WaitForSingleObject can indicate why the calling thread became schedulable again. If the object the thread is waiting for becomes notified, the return value is WAIT_OBJECT_0. If the set timeout has expired, the return value is WAIT_TIMEOUT. If an incorrect value (such as an invalid handle) is passed to WaitForSingleObject, the return value will be WAIT_FAILED (for more information, call GetLastError).

Guess you like

Origin blog.csdn.net/yohnyang/article/details/134592465