• 第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.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程序】多线程画随机矩形

    /*------------------------------------------------------------
       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空闲较多,没被充分利用

    /*------------------------------------------------------------
       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》

  • 相关阅读:
    升级xcode7.0 第三方库不能用的解决方法(bitcode是什么鬼?)
    java调用C#的dll
    微信JS SDK Demo
    【jQuery】smartMenu右键自定义上下文菜单插件(似web QQ)
    搭建Windows SVN服务器及TortoiseSVN使用帮助和下载
    微信jsSDK开发
    用c#开发微信(10) JSSDK 基本用法 分享接口“发送到朋友”
    MVC4.0网站发布和部署到IIS7.0上的方法
    C#的事件处理机制
    ASP.Net MVC开发基础学习笔记(1):走向MVC模式
  • 原文地址:https://www.cnblogs.com/5iedu/p/4715087.html
Copyright © 2020-2023  润新知