第20章 多任务和多线程(1)

20.1 进程和线程

联系与区别

进程

线程

概念

是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位(即进程是资源分配的最小单位)。是文件使用资源的总和(包含地址空间、代码、数据、对象句柄、环境变量和执行单元。进程不是一个可执行的实体。

线程是该进程中代码的“执行单元”,是操作系统分配处理器时间的基本单位(即是CPU调度的最小单位

关系

是线程的容器。一个进程必须要有一个主线程。

是进程的内容物。在进程中创建,不能脱离进程而单独存在,整个生命期都存在于进程中,进程被结束,线程也就自然被结束。

地址空间

进程间是相互独立的

同一进程的各线程间共享地址空间

资源分配

是资源分配的基本单位。同一进程中的线程共享其进程代码、数据及文件等资源

线程仅拥有自己的寄存器和栈,除了CPU和在线程中创建的窗口或安装钩子外,线程与任何系统资源分配都无关。

通信

进程间通信IPC

线程间的通信:直接读写进程数据段(如全局变量),但需要同步以保证数据的一致性。

调度和切换

进程切换时,耗费资源较大,效率要差

较快

消息队列

如果线程创建了一个窗口,Windows会单独给他分配一个消息队列,为了让窗口正常工作。则线程中必须实现消息循环。(这种情况在主线程和子线程都应该如此)。这也是控制台程序不需要消息循环的原因(因为没创建窗口)。

②如果在子线程中创建窗口,则主线程的消息循环根本不会获得这个窗口的消息,子线程必须设置窗口消息循环。当向窗口发送消息时,系统会先判断窗口是被哪个线程创建的,并把消息分派到正确线程的消息队列中。

③有了消息循环就必须遵守1/10秒规则,这就意味着线程不该用来处理长时间的工作。如果超过,可另开线程来处理。

20.2 Windows中的多线程

20.2.1  多线程的结构

(1)处理用户界面的线程

这些线程创建窗口并设置消息循环来负责处理窗口消息,一个进程中并不需要太多这种线程,一般让主线程负责这个工作就可以了。同时,这种线程不该处理1/10秒以上的工作

(2)工作线程

该类线程不处理窗口界面,当然也就不用处理消息了,它一般在后台运行,干一些繁重的、需要长时间运行的粗活,一般都要遵循1/10秒规则,但工作线程的要求相对比较宽松。

20.2.2 线程的麻烦

(1)竞争条件:不同的线程要访问同一资源,就不可避免会出现“竞争”,而问题解决的结果依赖于线程运行的时序,而不是程序的逻辑,这种情况称为竞争条件

(2)“死锁”:当两个程序己经中断彼此的运行,但是它们只有继续进行才能解除对方被中断的运行。

第20章 多任务和多线程(1) 

20.2.4 与线程有关的函数

(1)创建线程 CreateThread函数:返回值为线程的句柄

参数

含义

lpTreadAttributes

指向SECURITY_ATTRIBUTES结构,用来定义线程的安全属性。如果想让线程使用默认的安全属性,可设为NULL

dwStackSize

线程的堆栈大小。如果指定为0,那么线程的堆栈大小和主线程使用的大小相同。系统自动在进程的地址空间中为每个新线程分配私有的堆栈空间,这些空间在线程结束的时候会自动被释放,如果需要,会自动增大空间。

lpStartAddress

线程开始执行的地址。这个地址是一个规定格式的函数的入口地址,也被称为“线程函数”

dwParameter

传递给线程函数的自定义参数,可传递额外的数据

dwCreateFlags

创建标志。

0——线程创建后立即开始运行

CREATE_SUSPENDED——创建后处于挂起状态,直到调用函数ResumeThread去启动

lpThreadID

用来接收函数返回的线程ID(某些函数要用到线程ID)

(2)线程函数:DWORD WINAPI ThreadProc(LPVOID lpParameter);

参数

含义

lpParameter

该参数为CreateThread中dwParameter传入的参数。

(3)终止线程:线程结束后的退出码可被其他线程用GetExitCodeThread检测到。

方法

介绍

第1种方法

自然退出,当线程函数执行完毕后,就会完成线程的执行。

第2种方法

VOID ExitThread(dwExitCode ); //只能终止自己,不能终止另外一个线程。

其中dwExitCode是用户定义的,可以用别的线程用GetExitCodeThreads判断退出的类型。用法:如因某种原因需在线程函数内部提前退出线程,可在线程内部调用该函数,并指定dwExitCode以便让其他线程有机会判断。

第3种方法

TerminateThread(hThread,dwExitCode);

A、hThread:要终止的线程句柄,dwExitCode:用做被上线程的退出码。

B、TerminateThread是异步执行的,要确认线程己经真正结束,可用GetExitCodeThread来检测。

C、一般这个函数被强烈建议不要使用,因为一旦执行该函数,程序无法预测目标线程会在何处被中止,可能导致没有机会来做线程内部的一些清除工作。

第4种方法

ExitPorcess函数结束进程。——也应该避免使用,因为这种方法相当于对每个线程使用TerminateThread函数。

 ★几点注意

  ①般都应该尽量让线程自然结束,如果主线程要求某个线程结束,可以通过各种方法通知线程,线程收到通知后,做完扫尾工作后自行退出。只有在万不得己的情况下才使用TerminateThread函数去终止一个线程。

  ②当一个线程终止时,Windows释放线程所需的各种资源(如堆栈与寄存器环境),并且不再继续分配时间片,但线程对象并不马上释放,因为以后其他线程还需要GetExitCodeThread函数检测线程的退出码。线程对象会一直保存到CloseHandle才关闭

(4)挂起线程SuspendThread(hThread)

  系统为每个线程维护一个暂停计数器,SuspendThread一次,计数器加1。如果计数器的值大于0,则不会安排时间片。即线程处理挂起。

(5)恢复(唤醒)线程:ResumeThread(hThread):计数器减1,直到0时,才唤醒。

  ★一个线程可以将别的线程挂起,也可以自己挂起。但一旦挂起,就必须由别人来唤醒,自己无法恢复自己(因为该线程没时间片,就不能执行任何函数了)

(6)获取线程的退出码:GetExitCode(hThread,&ExitCode);

  ①当一个线程没有结束的时候,ExitCode==STILL_ACTIVE。

  ②如果结束了,则ExitCode返回的是由终止线程 (如ExitThread函数)中指定的值或线程函数中的返回值或进程的退出码。

20.2.5 多线程应用举例

(1)【RndRctMT程序】多线程画随机矩形

第20章 多任务和多线程(1)

/*------------------------------------------------------------
   RNDRCTMT.C -- Displays Random Rectangles
                 (c) Charles Petzold, 1998
  ------------------------------------------------------------*/

#include <windows.h>
#include <process.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

HWND hwnd;
int cxClient, cyClient;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("RndRctMT") ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }

     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("Random Rectangles"), // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;

     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

VOID ThreadProc(PVOID pVoid)
{
    HBRUSH hBrush;
    HDC hdc;
    int xLeft, xRight, yTop, yBottom, iRed, iGreen, iBlue;

    while (TRUE)
    {
        if (cxClient != 0 || cyClient !=0)
        {
            xLeft    = rand() % cxClient;
            xRight   = rand() % cxClient;
            yTop = rand() % cyClient;
            yBottom = rand() % cyClient;

            iRed = rand() & 255; //取出最低位8位
            iGreen = rand() & 255;
            iBlue = rand() & 255;

            hdc = GetDC(hwnd);
            hBrush = CreateSolidBrush(RGB(iRed, iGreen, iBlue));
            SelectObject(hdc, hBrush);

            Rectangle(hdc, min(xLeft, xRight), min(yTop, yBottom),
                           max(xLeft, xRight), max(yTop, yBottom));

            DeleteObject(hBrush);
            ReleaseDC(hwnd, hdc);
        }

    }
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{  
     switch (message)
     {
     case WM_CREATE:
          _beginthread(ThreadProc, 0, NULL); //需包含process.h文件
          return 0 ;

     case WM_SIZE:
         cxClient = LOWORD(lParam);
         cyClient = HIWORD(lParam);
         return 0;

     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

(2)【Multi1】编程竞赛问题——利用计时器来模拟多线程(但不是真正的多线程程序)

  ①当移动窗口或改变窗口大小时,所有的窗口输出会停止(因为要处理主窗口的消息)

  ②如果在所有的任务能在一条WM_TIMER处理完,并有空余,就意味着没有充分利用CPU的计算能力。(此时CPU空闲较多,没被充分利用

第20章 多任务和多线程(1)

/*------------------------------------------------------------
   MULTI1.C -- Multitasking Demo(使用计时器模拟多线程)
                 (c) Charles Petzold, 1998
  ------------------------------------------------------------*/

#include <windows.h>
#include <math.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; //主窗口过程
LRESULT APIENTRY WndProc1(HWND, UINT, WPARAM, LPARAM);  //窗口过程1(显示自然数递增的)
LRESULT APIENTRY WndProc2(HWND, UINT, WPARAM, LPARAM);  //窗口过程2(质数递增)
LRESULT APIENTRY WndProc3(HWND, UINT, WPARAM, LPARAM);  //窗口过程3(斐波那契数)
LRESULT APIENTRY WndProc4(HWND, UINT, WPARAM, LPARAM);  //窗口过程3(随机半径的圆)

int cyChar;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("Multi1") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }

     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("Multitasking Demo"), // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;

     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static HWND hwndChild[4];
     static WNDPROC ChildProc[] = { WndProc1, WndProc2, WndProc3, WndProc4 };
     static TCHAR* szChildClass[] = { TEXT("Child1"), TEXT("Child2"), TEXT("Child3"), TEXT("Child4") };

     HINSTANCE  hInstance;
     int i, cxClient, cyClient;
     WNDCLASS wndclass;

     switch (message)
     {
     case WM_CREATE:
          hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

          wndclass.style = CS_HREDRAW | CS_VREDRAW;
          wndclass.cbClsExtra = 0;
          wndclass.cbWndExtra = 0;
          wndclass.hInstance = hInstance;
          wndclass.hIcon = NULL;
          wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
          wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
          wndclass.lpszMenuName = NULL;

          //创建四个子窗口
          for (i = 0; i < 4; i++)
          {
              wndclass.lpfnWndProc = ChildProc[i];
              wndclass.lpszClassName = szChildClass[i];

              //注册窗口类
              RegisterClass(&wndclass);

              hwndChild[i] = CreateWindow(szChildClass[i], NULL, 
                                          WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE, //带边框
                                          0,0,0,0,
                                          hwnd,(HMENU)i,hInstance,NULL);

          }

          cyChar = HIWORD(GetDialogBaseUnits());
          SetTimer(hwnd, 1, 10, NULL);
          return 0 ;

     case WM_SIZE:
         cxClient = LOWORD(lParam);
         cyClient = HIWORD(lParam);

         for (i = 0; i < 4; i++)
             MoveWindow(hwndChild[i], (i % 2)*cxClient / 2, 
                                       (i>1)*cyClient/2,
                                       cxClient/2,cyClient/2,TRUE);


         return 0;

     case WM_TIMER:
         //每个子窗体发送WM_TIMER消息,也可以是其他自定义的消息,主要是触发绘图用的
         for (i = 0; i < 4; i++){
             SendMessage(hwndChild[i], WM_TIMER, wParam, lParam);
         }

         return 0;

     case WM_CHAR:
         if (wParam == 'x1B')  //ESC
             DestroyWindow(hwnd);
         return 0;

     case WM_DESTROY:
          KillTimer(hwnd, 1);
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

int CheckBottom(HWND hwnd, int cyClient, int iLine)
{
    //如果当前是最后一行
    if (iLine*cyChar + cyChar >cyClient) 
    {
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        iLine = 0;
    }
    return iLine;
}

//窗口过程1(显示自然数递增)
LRESULT APIENTRY WndProc1(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static int cyClient, iLine,iNum;
    TCHAR szBuffer[16];

    HDC hdc;

    switch (message)
    {
    case WM_SIZE:
        cyClient = HIWORD(lParam);
        return 0;

    case WM_TIMER:   
        hdc = GetDC(hwnd);

        //注意,这里该子窗口不清屏,会从上到下依次输出,直到最底部时,由CheckBottom判断,并清屏。
        iLine = CheckBottom(hwnd, cyClient, iLine); 
        TextOut(hdc, 0, iLine*cyChar, szBuffer, 
                wsprintf(szBuffer,TEXT("%d"),iNum++));

        iLine++;
        ReleaseDC(hwnd, hdc);
        return 0;

    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

//窗口过程2(质数递增)
LRESULT APIENTRY WndProc2(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static int cyClient, iLine, iNum=1;
    TCHAR szBuffer[16];
    int i, iSqrt;
    HDC hdc;

    switch (message)
    {
    case WM_SIZE:
        cyClient = HIWORD(lParam);
        return 0;

    case WM_TIMER:
        hdc = GetDC(hwnd);

        //
        do
        {
            //iNum是静态变量,所以每找到一个,会被保留下来,下次是从iNum接下去找
            if (++iNum < 0)   //iNum递增超过最大整数时(出现负数),则从新开始
                iNum = 0;

            //判断iNum是不是质数
            iSqrt = (int)sqrt(iNum);
            for (i = 2; i <=iSqrt; i++)
            {
                //能整除,说明不是质数
                if (iNum % i == 0)
                    break;  //跳出for,找下个iNum
            }

        } while (i<=iSqrt);

        //注意,这里该子窗口不清屏,会从上到下依次输出,直到最底部时,由CheckBottom判断,并清屏。
        iLine = CheckBottom(hwnd, cyClient, iLine);
        TextOut(hdc, 0, iLine*cyChar, szBuffer,
            wsprintf(szBuffer, TEXT("%d"), iNum++));

        iLine++;
        ReleaseDC(hwnd, hdc);
        return 0;

    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

//窗口过程3(斐波那契数)
LRESULT APIENTRY WndProc3(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static int cyClient, iLine, iNum=0,iNext=1;
    int iTemp;
    TCHAR szBuffer[16];

    HDC hdc;

    switch (message)
    {
    case WM_SIZE:
        cyClient = HIWORD(lParam);
        return 0;

    case WM_TIMER:

        //随着WM_TIMER时增加,依次计算斐波那契数列的各个值。
        if (iNum<0)  //当iNum超过整数的最大值时(变为负数),则从新开始计算
        {
            iNum = 0;
            iNext = 1;
        }

        //输出
        hdc = GetDC(hwnd);

        //注意,这里该子窗口不清屏,会从上到下依次输出,直到最底部时,由CheckBottom判断,并清屏。
        iLine = CheckBottom(hwnd, cyClient, iLine);
        TextOut(hdc, 0, iLine*cyChar, szBuffer,
            wsprintf(szBuffer, TEXT("%d"), iNum));
        iLine++;
        ReleaseDC(hwnd, hdc);

        iTemp = iNum;
        iNum = iNext;
        iNext += iTemp;
        return 0;

    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

//窗口过程4(画随机半径的圆)
LRESULT APIENTRY WndProc4(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static int cxClient, cyClient;
    int iDiameter; //直径

    HDC hdc;

    switch (message)
    {
    case WM_SIZE:
        cyClient = HIWORD(lParam);
        cxClient = LOWORD(lParam);
        return 0;

    case WM_TIMER:
        InvalidateRect(hwnd, NULL, TRUE); //每次绘画之前,先清屏
        UpdateWindow(hwnd); //立即把背景刷白

        //计算圆的直径
        iDiameter = rand() % (max(1,min(cxClient,cyClient)));

        hdc = GetDC(hwnd);

        Ellipse(hdc, (cxClient - iDiameter) / 2, 
                     (cyClient - iDiameter) / 2,
                     (cxClient + iDiameter) / 2,
                     (cyClient + iDiameter) / 2);
        ReleaseDC(hwnd, hdc);
        return 0;

    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

(3)【Multi2】编程竞赛问题——利用多线程技术

  ①在每个子窗口过程的WM_CREATE中开辟一个独立的工作线程,线程函数名如Thread1等。

  ②_beginthread的第三个参数用来将子窗口的一个结构体传到线程函数中去。这个结构体有五个字段——一个窗口句柄、窗口宽度和高度、字符高度和一个是否中止线程的布尔变量。

  ③一般线程退出线程函数后,会自动销毁线程,但如果线程处于一个很复杂的处理过程中,调用_endthread是很有用的。

  ④与Multi1程序相比,在拖动主窗口或改变窗口大小时,所有子窗口的输出不会停止。

  ⑤以WndProc1和Thread1为例,分析该线程存在的两个问题:

    A、以WndProc1在主线程,Thread1在子线程中运行,两者是并行的。这两个线程之间的切换是不可预知的。假设Thread1刚执行到检查pParams->bKill是否为TRUE时(设此时非TRUE),但这时被切换到主线程,而就在这里,用户按ESC终止了程序。WndProc1会收到WM_DESTORY消息,将bKill设为TRUE。但太晚了,设这时,操作系统突然又切换到Thread1函数中,在这个函数里会试图获取一个不存在的窗口的设备环境。

    B、另一个问题就是主线程可能收到WM_ERASEBKGND或WM_PAINT消息。但这时子线程正在绘图输出。(这个问题Windows帮我们用串行化处理了),但有些GDI函数是非串行化的,如画笔、画刷、字体,位图、区域和调色板。有可能出现一个线程销毁了另一个线程正在使用的对象。——使用临界区或不要在线程间共享GDI对象。

【效果图】与Multi1程序一样,但运行速度更快了 

/*------------------------------------------------------------
   MULTI2.C -- Multitasking Demo(真正的多线程程序)
                 (c) Charles Petzold, 1998
  ------------------------------------------------------------*/

#include <windows.h>
#include <math.h>
#include <process.h>  //_beginthread函数要用到该头文件

//供线程函数使用的额外数据结构
typedef struct
{
    HWND hwnd;
    int cxClient;
    int cyClient;
    int cyChar;
    BOOL bKill;    //用来决定是否中止线程。
                   //主线程按ESC键时,在退出程序前,各线程根据这个
                   //标出退出线程函数,当然也就退出了线程。
}PARAMS,*PPARAMS;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; //主窗口过程
LRESULT APIENTRY WndProc1(HWND, UINT, WPARAM, LPARAM);  //窗口过程1(显示自然数递增的)
LRESULT APIENTRY WndProc2(HWND, UINT, WPARAM, LPARAM);  //窗口过程2(质数递增)
LRESULT APIENTRY WndProc3(HWND, UINT, WPARAM, LPARAM);  //窗口过程3(斐波那契数)
LRESULT APIENTRY WndProc4(HWND, UINT, WPARAM, LPARAM);  //窗口过程3(随机半径的圆)

void Thread1(PVOID pvoid);   //线程函数1(显示自然数递增的)
void Thread2(PVOID pvoid);   //线程函数2(质数递增)
void Thread3(PVOID pvoid);   //线程函数3(斐波那契数)
void Thread4(PVOID pvoid);   //线程函数3(随机半径的圆)

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("Multi2") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }

     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("Multitasking Demo"), // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;

     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static HWND hwndChild[4];
     static WNDPROC ChildProc[] = { WndProc1, WndProc2, WndProc3, WndProc4 };
     static TCHAR* szChildClass[] = { TEXT("Child1"), TEXT("Child2"), TEXT("Child3"), TEXT("Child4") };

     HINSTANCE  hInstance;
     int i, cxClient, cyClient;
     WNDCLASS wndclass;

     switch (message)
     {
     case WM_CREATE:
          hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

          wndclass.style = CS_HREDRAW | CS_VREDRAW;
          wndclass.cbClsExtra = 0;
          wndclass.cbWndExtra = 0;
          wndclass.hInstance = hInstance;
          wndclass.hIcon = NULL;
          wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
          wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
          wndclass.lpszMenuName = NULL;

          //创建四个子窗口
          for (i = 0; i < 4; i++)
          {
              wndclass.lpfnWndProc = ChildProc[i];
              wndclass.lpszClassName = szChildClass[i];

              //注册窗口类
              RegisterClass(&wndclass);

              hwndChild[i] = CreateWindow(szChildClass[i], NULL, 
                                          WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE, //带边框
                                          0,0,0,0,
                                          hwnd,(HMENU)i,hInstance,NULL);

          }
          return 0 ;

     case WM_SIZE:
         cxClient = LOWORD(lParam);
         cyClient = HIWORD(lParam);

         for (i = 0; i < 4; i++)
             MoveWindow(hwndChild[i], (i % 2)*cxClient / 2, 
                                       (i>1)*cyClient/2,
                                       cxClient/2,cyClient/2,TRUE);         
         return 0;

     case WM_CHAR:
         if (wParam == 'x1B')  //ESC
             DestroyWindow(hwnd);
         return 0;

     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}

int CheckBottom(HWND hwnd, int cyClient, int cyChar ,int iLine)
{
    //如果当前是最后一行
    if (iLine*cyChar + cyChar >cyClient) 
    {
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        iLine = 0;
    }
    return iLine;
}

//窗口过程1(显示自然数递增)
LRESULT APIENTRY WndProc1(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static PARAMS params;
    switch (message)
    {
    case WM_CREATE:
        params.hwnd = hwnd;
        params.cyChar = HIWORD(GetDialogBaseUnits());
        params.bKill = FALSE;

        _beginthread(Thread1, 0, &params);  //带额外参数,注意_endthread()在线程函数中调用。

        return 0;
    case WM_SIZE:
        params.cyClient = HIWORD(lParam);
        params.cxClient = LOWORD(lParam);
        return 0;

    case WM_DESTROY:
        params.bKill = TRUE; 
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

//窗口过程2(质数递增)
LRESULT APIENTRY WndProc2(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static PARAMS params;
    switch (message)
    {
    case WM_CREATE:
        params.hwnd = hwnd;
        params.cyChar = HIWORD(GetDialogBaseUnits());
        params.bKill = FALSE;

        _beginthread(Thread2, 0, &params); //带额外参数,注意_endthread()在线程函数中调用。

        return 0;
    case WM_SIZE:
        params.cyClient = HIWORD(lParam);
        params.cxClient = LOWORD(lParam);
        return 0;

    case WM_DESTROY:
        params.bKill = TRUE;
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

//窗口过程3(斐波那契数)
LRESULT APIENTRY WndProc3(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static PARAMS params;
    switch (message)
    {
    case WM_CREATE:
        params.hwnd = hwnd;
        params.cyChar = HIWORD(GetDialogBaseUnits());
        params.bKill = FALSE;

        _beginthread(Thread3, 0, &params);  //带额外参数,注意_endthread()在线程函数中调用。

        return 0;
    case WM_SIZE:
        params.cyClient = HIWORD(lParam);
        params.cxClient = LOWORD(lParam);
        return 0;

    case WM_DESTROY:
        params.bKill = TRUE;
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

//窗口过程4(画随机半径的圆)
LRESULT APIENTRY WndProc4(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static PARAMS params;
    switch (message)
    {
    case WM_CREATE:
        params.hwnd = hwnd;
        params.cyChar = HIWORD(GetDialogBaseUnits());
        params.bKill = FALSE;

        _beginthread(Thread4, 0, &params);  //带额外参数,注意_endthread()在线程函数中调用。

        return 0;
    case WM_SIZE:
        params.cyClient = HIWORD(lParam);
        params.cxClient = LOWORD(lParam);
        return 0;

    case WM_DESTROY:
        params.bKill = TRUE;
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}
//线程函数1(显示自然数递增)
void Thread1(PVOID pvoid)
{
    PPARAMS pParams;
    TCHAR szBuffer[16];
    HDC hdc;
    int iLine = 0,iNum=0;

    pParams = (PPARAMS)pvoid;

    while (!pParams->bKill)  //用户如果没要出退出线程时
    {
        hdc = GetDC(pParams->hwnd);

        //注意,这里该子窗口不清屏,会从上到下依次输出,直到最底部时,由CheckBottom判断,并清屏。
        iLine = CheckBottom(pParams->hwnd, pParams->cyClient, pParams->cyChar, iLine);
        TextOut(hdc, 0, iLine*pParams->cyChar, szBuffer,
            wsprintf(szBuffer, TEXT("%d"), iNum++));

        iLine++;
        ReleaseDC(pParams->hwnd, hdc);
    }
    _endthread(); //一般退出该线程函数后,线程会自动销毁,但在很复杂的线程
                  //环境中,用这个函数有时很有用
}

//线程函数2(质数递增)
void Thread2(PVOID pvoid)
{
    PPARAMS pParams;
    TCHAR szBuffer[16];
    HDC hdc;
    int iLine = 0, iNum = 0,i,iSqrt;

    pParams = (PPARAMS)pvoid;

    while (!pParams->bKill) //用户如果没要出退出线程时
    {
        hdc = GetDC(pParams->hwnd);

        do
        {
            //iNum是静态变量,所以每找到一个,会被保留下来,下次是从iNum接下去找
            if (++iNum < 0)   //iNum递增超过最大整数时(出现负数),则从新开始
                iNum = 0;

            //判断iNum是不是质数
            iSqrt = (int)sqrt(iNum);
            for (i = 2; i <= iSqrt; i++)
            {
                //能整除,说明不是质数
                if (iNum % i == 0)
                    break;  //跳出for,找下个iNum
            }

        } while (i <= iSqrt);

        //注意,这里该子窗口不清屏,会从上到下依次输出,直到最底部时,由CheckBottom判断,并清屏。
        iLine = CheckBottom(pParams->hwnd, pParams->cyClient, pParams->cyChar, iLine);
        TextOut(hdc, 0, iLine*pParams->cyChar, szBuffer,
            wsprintf(szBuffer, TEXT("%d"), iNum++));

        iLine++;
        ReleaseDC(pParams->hwnd, hdc);
    }
    _endthread();//一般退出该线程函数后,线程会自动销毁,但在很复杂的线程
                  //环境中,用这个函数有时很有用
}

//线程函数3(斐波那契数)
void Thread3(PVOID pvoid)
{
    PPARAMS pParams;
    int iLine=0, iNum=0,iNext=1;
    int iTemp;
    TCHAR szBuffer[16];

    HDC hdc;

    pParams = (PPARAMS)pvoid;
    while (!pParams->bKill) //用户如果没要出退出线程时
    {
        //依次计算斐波那契数列的各个值。
        if (iNum<0)  //当iNum超过整数的最大值时(变为负数),则从新开始计算
        {
            iNum = 0;
            iNext = 1;
        }

        //输出
        hdc = GetDC(pParams->hwnd);

        //注意,这里该子窗口不清屏,会从上到下依次输出,直到最底部时,由CheckBottom判断,并清屏。
        iLine = CheckBottom(pParams->hwnd, pParams->cyClient, pParams->cyChar, iLine);
        TextOut(hdc, 0, iLine*pParams->cyChar, szBuffer,
            wsprintf(szBuffer, TEXT("%d"), iNum));
        iLine++;
        ReleaseDC(pParams->hwnd, hdc);

        iTemp = iNum;
        iNum = iNext;
        iNext += iTemp;
    }
    _endthread();//一般退出该线程函数后,线程会自动销毁,但在很复杂的线程
                  //环境中,用这个函数有时很有用
}

//线程函数4(画随机半径的圆)
void Thread4(PVOID pvoid)
{
    PPARAMS pParams;
    int iDiameter; //直径

    HDC hdc;

    pParams = (PPARAMS)pvoid;
    while (!pParams->bKill) //用户如果没要出退出线程时
    {
        InvalidateRect(pParams->hwnd, NULL, TRUE); //每次绘画之前,先清屏
        UpdateWindow(pParams->hwnd); //立即把背景刷白

        //计算圆的直径
        iDiameter = rand() % (max(1, min(pParams->cxClient, pParams->cyClient)));

        hdc = GetDC(pParams->hwnd);

        Ellipse(hdc, (pParams->cxClient - iDiameter) / 2,
                     (pParams->cyClient - iDiameter) / 2,
                     (pParams->cxClient + iDiameter) / 2,
                     (pParams->cyClient + iDiameter) / 2);
        ReleaseDC(pParams->hwnd, hdc);
    }
    _endthread();//一般退出该线程函数后,线程会自动销毁,但在很复杂的线程
                  //环境中,用这个函数有时很有用
}

20.2.6 休眼的好处

(1)多线程最好的架构是让主线程创立所有的窗口,拥有所有的窗口过程和消息处理。子线程为工作线程,工作于后台。正因为如此,在主线程创建动画很容易,直接在WM_TIMER就可实现。但子线程没有窗口,没有消息处理,也就没有了时间控制,因线程切换一般很快,所以动画会运行过快

(2)解决方法就是Sleep。调用Sleep时,线程会主动挂起在指定的时间段里,系统不会为其分配时间片(当传入的参数为0,使得现有的时间段作废,即马上挂起等待下一个时间片的到来)

(3)主线程中,不应该使用Sleep函数因为这会使主线程交给时间片,减慢消息的处理。当然,如果主线程没创立窗口(没有消息处理),那还是可以Sleep的。

(4)最牢靠而最立即的警告就是,千万不要在一个 CriticalSection 之中调用 Sleep() 或任何 Wait...() API 函数。(见《Win32多线程程序设计P101》

原文链接: https://www.cnblogs.com/5iedu/p/4715087.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    第20章 多任务和多线程(1)

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/394690

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月3日 下午3:52
下一篇 2023年4月3日 下午3:52

相关推荐