【Win32多线程】如何避免线程等待浪费CPU时间,降低对系统资源的冲击?

如何避免线程浪费CPU时间?

等待是线程的必要之恶。

两个等待技术

1.Win32的Sleep()函数,要求操作系统终止线程动作,直到度过某个指定时间之后才恢复。(不能事先知道等多久) 

 2.busy loop,不断调用GetExitCodeThread(),直到其结果不再是STILL_ACTIVE.(缺点浪费CPU时间),绝对不要在Win32中使用busy loop

下面的程序示范采用busy loop方式的耗时:

#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <time.h>
DWORD WINAPI ThreadFunc(LPVOID);

int main()
{
    HANDLE hThrd;
    DWORD exitCode = 0;
    DWORD threadId;
    DWORD begin;
    DWORD elapsed;


    puts("Timing normal function call...");
    begin = GetTickCount();
    ThreadFunc(0); //直接调用,花费时间0.063秒
    elapsed = GetTickCount()-begin;
    printf("Function call took: %d.%.03d seconds\n\n",
                elapsed/1000, elapsed%1000);
    
    puts("Timing thread + busy loop...");
    begin = GetTickCount();

    hThrd = CreateThread(NULL,
        0,
        ThreadFunc,
        (LPVOID)1,
        0,
        &threadId );
    
    /* This busy loop chews up lots of CPU time消耗时间最多 */
    for (;;)
    {
        GetExitCodeThread(hThrd, &exitCode);
        if ( exitCode != STILL_ACTIVE )
            break;
    }


    elapsed = GetTickCount()-begin;
    printf("Thread + busy loop took: %d.%.03d seconds\n",
                elapsed/1000, elapsed%1000); //总共花费20.062秒


    CloseHandle(hThrd);


    return EXIT_SUCCESS;
}




/*
 * Cute little busy work routine that computes the value
 * of PI using probability.  Highly dependent on having
 * a good random number generator (rand is iffy)
 */
DWORD WINAPI ThreadFunc(LPVOID n)
{
    int i;
    int inside = 0;
    double val;


    UNREFERENCED_PARAMETER(n);


    /* Seed the random-number generator */
    srand( (unsigned)time( NULL ) );


    for (i=0; i<1000000; i++)
    {
        double x = (double)(rand())/RAND_MAX;
        double y = (double)(rand())/RAND_MAX;
        if ( (x*x + y*y) <= 1.0 )
            inside++;
    }
    val = (double)inside / i;
    printf("PI = %.4g\n", val*4);
	Sleep((DWORD)n*10000*2);




    return 0;
}

Windows 7 性能监视器
  1、开始-运行(或者按Win+R键打开运行对话框)


  2、输入“perfmon”, 然后回车


  就可以进入“性能监视器”


它可以告诉我们CPU的忙碌程度,它是一个重要的工具。用以确定我们所写的程序的预期行为。
 


等待一个线程的结束:win32的改善方法


我们需要一个新版本的Sleep(),它能够在某个线程结束时被调用。WaitForSingleObject()函数可以这么做,第一个参数为核心对象的handle


DWORD WaitForSingleObject(
  HANDLE hHandle,        // handle to object
  DWORD dwMilliseconds   // time-out interval指定最长的等待时间
);
放置一个“线程核心对象”#2作为参数,将使线程#1开始睡眠,直到线程#2结束为止。

之前的busy loop 可以用下面代码取代:WaitForSingleObject(hThrd,INFINITE);
Win32 中大部分HANDLE表示的对象都能够作为WaitForSingleObject()的等到目标。

被激发的对象(Signaled Objects)

什么是被激发对象??
可被WaitForSingleObject()使用的核心对象有两种状态:激发与未被激发。WaitForSingleObject()会在目标物变成激发状态返回。当核心对象被激发时,会导致WaitForSingleObject()醒来。Wait()函数也是如此. 当线程正在执行时,线程对象处于激发状态。当线程结束时,线程对象被激发了。因此,任何线程如果等待的是一个线程对象,将会在等待对象结束时被调用,因为当时线程对象自动变成激发状态

线程如何应付激发对象?
------------------------等待多个对象---------------------------
写一个程序,使用最多三个线程来完成六项工作。函数名为ThreadFunc()用来执行某些事情然后返回。工作的执行是靠调用Sleep()来模拟。只要一个线程结束,就会有另外一个线程被产生做下个工作。

WaitForMultipleObject()允许你在同一个时间等待一个以上的对象。必须将一个由handles组成的数组交给此函数,并指定其中一个对象或是全部的对象。
DWORD WaitForMultipleObjects(
  DWORD nCount,             // number of handles in array
  CONST HANDLE *lpHandles,  // object-handle array
  BOOL fWaitAll,            // wait option 当为TRUE,表示所有的handles都必须激发,此函数才得以返回,否则任何一个handle激发时就返回。
  DWORD dwMilliseconds      // time-out interval
);


handles数组中的元素个数有上限,不能超过MAXIMUM_WAIT_OBJECTS.

/*
 * 
 *等待多个线程,监视多个线程的状态
 * Sample code for "Multithreading Applications in Win32"
 * 
 *
 * Call ThreadFunc NUM_TASKS times, using
 * no more than THREAD_POOL_SIZE threads.
 * This version uses WaitForMultipleObjects
 * to provide a more optimal solution.
 *
 * 
 */


#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>


DWORD WINAPI ThreadFunc(LPVOID);


#define THREAD_POOL_SIZE 3
#define MAX_THREAD_INDEX THREAD_POOL_SIZE-1
#define NUM_TASKS 6


int main()
{
    HANDLE  hThrds[THREAD_POOL_SIZE];
    int     slot = 0;
    DWORD   threadId;
    int     i;
    DWORD   rc;


    for (i=1; i<=NUM_TASKS; i++)
    {
        /* Until we've used all threads in *
         * the pool, do not need to wait   *
         * for one to exit                 */
        if (i > THREAD_POOL_SIZE)
        {
            /* Wait for one thread to terminate */
            rc = WaitForMultipleObjects(
                THREAD_POOL_SIZE,
                hThrds,
                FALSE,
                INFINITE );
            slot = rc - WAIT_OBJECT_0; //得到具体的handles下标
            /*MTVERIFY( slot >= 0 
                && slot < THREAD_POOL_SIZE );*/
            printf("Slot %d terminated\n", slot );
             CloseHandle(hThrds[slot]);
        }
        /* Create a new thread in the given
         * available slot */
      hThrds[slot] = CreateThread(NULL,
            0,
            ThreadFunc,
            (LPVOID)slot,
            0,
            &threadId );
        printf("Launched thread #%d (slot %d)\n", i, slot);
        slot++;
    }


    /* Now wait for all threads to terminate */
    rc = WaitForMultipleObjects(
        THREAD_POOL_SIZE,
        hThrds,
        TRUE,
        INFINITE );
   
    for (slot=0; slot<THREAD_POOL_SIZE; slot++)
       CloseHandle(hThrds[slot]);
    printf("All slots terminated\n");


    return EXIT_SUCCESS;
}


/*
 * This function just calls Sleep for
 * a random amount of time, thereby
 * simulating some task that takes time.
 *
 * The param "n" is the index into
 * the handle array, kept for informational
 * purposes.
 */
DWORD WINAPI ThreadFunc(LPVOID n)
{
    srand( GetTickCount() );


    Sleep((rand()%10)*800+500);
    printf("Slot %d idle\n", n);
    return ((DWORD)n);
}



------------------------在一个GUI程序中等待---------------------------
Windows中的标准消息循环如下:
while (GetMessage(&msg, NULL, 0, 0))  
    {   // Get Next message in queue  
        if(hDlgMain == NULL || !IsDialogMessage(hDlgMain,&msg))  
        {  
            TranslateMessage(&msg); /* Translate virtual key codes */  
            DispatchMessage(&msg);  /* Dispatches message to window */  
        }  
    } 


GetMessage()有点像特殊版本的WaitForSingleObject(),它等待消息而不是核心对象。一旦调用GetMessage(),除非有一个消息真正进入你的消息队列,否则它不会返回。


如何在主线程等待一个Handle?
如果使用WaitForSingleObject()或WaitForMultipleObjects()等待某个对象激发,则没有办法回到主消息循环中去。
为了解决这个问题,主消息循环必须修改,使它同时等待消息或是核心对象被激发。必须使用一个MsgWaitForMultipleObjects()函数。它会在“对象被激发”或“消息到达队列”时被唤醒而返回。


MsgWaitForMultipleObjects()多接受一个参数,指定哪些消息是观察对象。
DWORD MsgWaitForMultipleObjects(
  DWORD nCount,          // number of handles in array
  CONST HANDLE pHandles, // object-handle array
  BOOL fWaitAll,         // wait option
  DWORD dwMilliseconds,  // time-out interval
  DWORD dwWakeMask       // input-event type 欲观测的用户输入消息
);



重建消息循环:
代码示例:更改后台打印

/*
 * 
 * Sample code for "Multithreading Applications in Win32"
 * This is from Chapter 3, Listing 3-4
 *
 * Demonstrate background printing and
 * using MsgWaitForMultipleObjects to
 * wait for threads to exit.
 */


#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include "resource.h"
#include "MtVerify.h"


//
// Macro definitions
//
#define WM_SHOWBITMAP   WM_APP
#define WM_THREADCOUNT  WM_APP+1


#define MAX_PRINT_JOBS  64




//
// Structures
//
typedef struct
{   // Information passed to background thread for printing
    HWND hDlg;
    HWND hWndParent;
    HDC hDc;
    BOOL bPrint;    // TRUE if printing;
    char szText[256];
} ThreadPrintInfo;


//
// Global variables
//
HANDLE hInst;
HBITMAP gbmpDisplay;
RECT gDisplayRect;


int gNumPrinting = 0;


// Handle to each created thread
HANDLE gPrintJobs[64];


// Height of bitmap returned by DrawText
int iHeight;


// HWND of the dialog so other threads can find it.
HWND hDlgMain;




//
// Function declarations
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK MainWndProc(HWND hWnd, unsigned msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PrintDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL PrintDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam);
void PrintDlg_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify);
void PrintDlg_OnPaint(HWND hwnd);
void PrintText(HWND hwndParent, char *pszText);
void PrintToDisplay(HWND hwndParent, char *pszText);
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
DWORD WINAPI BackgroundPrintThread(LPVOID pVoid);




///
//
//      WinMain
//
// Main entry point of application. This will be a
// dialog based app, not a normal window, so this
// routine acts a little differently than "normal".
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG     msg;
    HWND    hWnd;
    WNDCLASS wc;
    BOOL    quit;
    int     exitCode;


    hInst = hInstance;
    if (!hPrevInstance)
    {
        memset(&wc, 0, sizeof(wc));
        wc.lpfnWndProc  = MainWndProc;
        wc.hInstance    = hInstance;
        wc.hIcon        = LoadIcon (hInstance, "GenIco");
        wc.hCursor      = LoadCursor(NULL,IDC_ARROW);
        wc.hbrBackground= GetSysColorBrush(COLOR_BACKGROUND);
        wc.lpszMenuName = "PRINTING_MENU";
        wc.lpszClassName= "PrintDlgClass";
        if (!RegisterClass(&wc))
            return FALSE;
    }




    hWnd = CreateWindow(
        "PrintDlgClass",
        "Printing Hands-On",
        WS_OVERLAPPED|WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU,
        CW_USEDEFAULT, // At this point we do not want to
        0,             //  show the window until we know
        0,             //  how big the Dialog Box is so
        0,             //  that we can fit the main window
        NULL,          //  around it.
        NULL,
        hInstance,
        NULL);


    hDlgMain = CreateDialog(hInst,
                    MAKEINTRESOURCE(IDD_PRINT),
                    hWnd, PrintDlgProc);


    ShowWindow(hWnd, nCmdShow);
    ShowWindow(hDlgMain, SW_SHOW);


    quit = FALSE;
    exitCode = 0;
    while (!quit || gNumPrinting > 0)
    {   // Wait for next message or object being signaled
        DWORD   dwWake;
        dwWake = MsgWaitForMultipleObjects(
                                gNumPrinting,
                                gPrintJobs,
                                FALSE,
                                INFINITE,
                                QS_ALLEVENTS);


        if (dwWake >= WAIT_OBJECT_0 && dwWake < WAIT_OBJECT_0 + gNumPrinting)
        {   // Object has been signaled
            // Reorder the handle array so we do not leave
            // empty slots. Take the handle at the end of
            // the array and move it into the now-empty slot.
            int index = dwWake - WAIT_OBJECT_0;
            gPrintJobs[index] = gPrintJobs[gNumPrinting-1];
            gPrintJobs[gNumPrinting-1] = 0;
            gNumPrinting--;
            SendMessage(hDlgMain, WM_THREADCOUNT, gNumPrinting, 0L);
        } // end if
        else if (dwWake == WAIT_OBJECT_0 + gNumPrinting)
        {
            while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {   // Get Next message in queue
                if(hDlgMain == NULL || !IsDialogMessage(hDlgMain,&msg))
                {
                    if (msg.message == WM_QUIT)
                    {
                        quit = TRUE;
                        exitCode = msg.wParam;
                        break;
                    } // end if
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            } // end while
        }
    } // end while
    return (exitCode);  /* Returns the value from PostQuitMessage */
}




LRESULT CALLBACK MainWndProc(HWND hWnd, unsigned msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
        break;


    case WM_COMMAND:


        switch (wParam)
        {
        case IDM_ABOUT:
            DialogBox(hInst, "AboutBox", hWnd, (DLGPROC)About);
            break;
        case IDM_EXIT:
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wParam, lParam));
        }


    case WM_SETFOCUS:
        // ensure that the Dialog Box has the focus
        SetFocus(hDlgMain);
        break;


    case WM_DESTROY:
        PostQuitMessage(0);
        break;


    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);


    }
    return 0;
}




LRESULT CALLBACK PrintDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CLOSE:
        DestroyWindow(hDlg);
        hDlgMain = NULL;
        break;
        
    case WM_DESTROY:
        return TRUE;
        break;


    case WM_SHOWBITMAP:
        if (gbmpDisplay)
            DeleteObject(gbmpDisplay);


        gDisplayRect = *(RECT*)wParam;
        gbmpDisplay = (HBITMAP) lParam;
        InvalidateRect(hDlgMain, NULL, TRUE);
        break;


    case WM_THREADCOUNT:
        {
            HMENU hMenu;


            // Show number of threads
            SetDlgItemInt(hDlg, IDC_EDIT_THREADS, wParam, FALSE);


            // Enable/Disable File.Exit menu
            hMenu = GetMenu(GetParent(hDlg));
            EnableMenuItem(hMenu, IDM_EXIT, wParam != 0);
            break;
        } // end case


    HANDLE_MSG(hDlg, WM_INITDIALOG, PrintDlg_OnInitDialog);
    HANDLE_MSG(hDlg, WM_COMMAND, PrintDlg_OnCommand);
    HANDLE_MSG(hDlg, WM_PAINT, PrintDlg_OnPaint);


    default:
        return (FALSE);
    }


    return 0;
}


BOOL PrintDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam)
{
    RECT rect;


    // Size parent to fit this dialog
    GetWindowRect(hwndDlg, &rect); 
    SetWindowPos(GetParent(hwndDlg),NULL,
        0,0,
        rect.right-rect.left,
        rect.bottom-rect.top+GetSystemMetrics(SM_CYMENU)
            +GetSystemMetrics(SM_CYCAPTION),
        SWP_NOMOVE | SWP_NOZORDER);


    SetDlgItemInt(hwndDlg, IDC_EDIT_THREADS, 0, FALSE);


    return TRUE;
}


void PrintDlg_OnCommand(HWND hDlg, int id,HWND hwndCtl, UINT codeNotify)
{
    char szText[256];


    switch (id)
    {
    case IDC_PRINT:
        GetDlgItemText(hDlg, IDC_EDIT_TEXT, szText, 256);
        PrintText(hDlg, szText);
        break;


    case IDC_DISPLAY:
        GetDlgItemText(hDlg, IDC_EDIT_TEXT, szText, 256);
        PrintToDisplay(hDlg, szText);
        break;


    case IDCANCEL:
    case IDM_EXIT:
        PostMessage(GetParent(hDlg),WM_DESTROY,
                        (WPARAM)0, (LPARAM)0);
        DestroyWindow(hDlgMain);
        hDlgMain = NULL;
        break;
        
    default:
        break;
    }
}


void PrintDlg_OnPaint( HWND hwnd )
{
    PAINTSTRUCT paint;
    HWND hwndCtrl;
	HDC hdc;
    HDC hDcMem;
    HBITMAP bmpOld;
    RECT rect;
    POINT point;


	if (!gbmpDisplay)
		return;


    hwndCtrl = GetDlgItem(hwnd, IDC_OUTPUT);


    hdc = BeginPaint(hwnd, &paint);


    GetWindowRect(hwndCtrl, &rect);
    point = *((POINT *)&rect);
    ScreenToClient(hwnd, &point);


    hDcMem = CreateCompatibleDC(NULL);
    bmpOld = SelectObject(hDcMem, gbmpDisplay);


    // Copy bitmap to screen
    MTVERIFY( BitBlt(hdc, point.x+10, point.y+40,
        gDisplayRect.right-gDisplayRect.left, gDisplayRect.bottom-gDisplayRect.top,
        hDcMem, iHeight, 0, SRCCOPY) );


    SelectObject(hDcMem, bmpOld);
    DeleteDC(hDcMem);


    EndPaint(hwnd, &paint);
}


//
// Asks user which printer to use, then creates
// background printing thread.
//
void PrintText(HWND hwndParent, char *pszText)
{
    ThreadPrintInfo *pInfo;
    HANDLE hThread;
    DWORD dwThreadId;
    int result;
    DOCINFO docInfo;


    PRINTDLG dlgPrint;


    // Put up Common Dialog for Printing and get hDC.
    memset(&dlgPrint, 0, sizeof(PRINTDLG));
    dlgPrint.lStructSize = sizeof(PRINTDLG);
    dlgPrint.hwndOwner = hwndParent;
    dlgPrint.Flags = PD_ALLPAGES | PD_USEDEVMODECOPIES
           | PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC;
    dlgPrint.hInstance = hInst;
    if (!PrintDlg(&dlgPrint))
        return;


    // Initialize Printer device
    docInfo.cbSize = sizeof(DOCINFO);
    docInfo.lpszDocName = "Background Printing Example";
    docInfo.lpszOutput = NULL;
    docInfo.lpszDatatype = NULL;
    docInfo.fwType = 0;
    result = StartDoc(dlgPrint.hDC, &docInfo);
    result = StartPage(dlgPrint.hDC);


    pInfo = HeapAlloc(GetProcessHeap(),
                      HEAP_ZERO_MEMORY,
                      sizeof(ThreadPrintInfo));
    pInfo->hDlg = hwndParent;
    pInfo->hWndParent = hwndParent;
    pInfo->hDc = dlgPrint.hDC;
    pInfo->bPrint = TRUE;
    strcpy(pInfo->szText, pszText);


    MTVERIFY( hThread = CreateThread(NULL, 0,
        BackgroundPrintThread, (LPVOID)pInfo,
        0, &dwThreadId ));


	// keep track of all background printing threads
    gPrintJobs[gNumPrinting++] = hThread;


    SendMessage(hwndParent, WM_THREADCOUNT, gNumPrinting, 0L);
}


//
// Shows output on the dialog box.
//
void PrintToDisplay(HWND hwndParent, char *pszText)
{
    ThreadPrintInfo *pInfo;
    DWORD dwThreadId;
    HANDLE hThread;


    pInfo = HeapAlloc(GetProcessHeap(),
                      HEAP_ZERO_MEMORY,
                      sizeof(ThreadPrintInfo));
    pInfo->hDlg = hwndParent;
    pInfo->hWndParent = GetDlgItem(hwndParent, IDC_OUTPUT);
	pInfo->hDc = GetDC(pInfo->hWndParent);
    pInfo->bPrint = FALSE;
    strcpy(pInfo->szText, pszText);


    MTVERIFY( hThread = CreateThread(NULL, 0,
                                     BackgroundPrintThread,
                                     (LPVOID)pInfo,
                                     0, &dwThreadId ));


	// keep track of all background printing threads
    gPrintJobs[gNumPrinting++] = hThread;


    SendMessage(hwndParent, WM_THREADCOUNT, gNumPrinting, 0L);
}






//---------------------------------------------------------
// About Box Handling
//---------------------------------------------------------


LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK
                || LOWORD(wParam) == IDCANCEL)
            {
                EndDialog(hDlg, TRUE);
                return (TRUE);
            }
            break;


        default:
            return (DefWindowProc(hDlg, message, wParam, lParam));
    }


    return FALSE;
}




//---------------------------------------------------------
// Background Printing Code
//---------------------------------------------------------


DWORD WINAPI BackgroundPrintThread(LPVOID pVoid)
{
    ThreadPrintInfo *pInfo = (ThreadPrintInfo*) pVoid; 
    RECT rect;
    RECT rectMem;
    HDC hDcMem;
    HBITMAP bmpMem;
    HBITMAP bmpOld;
    int x, y;
    int counter = 0;
    int nHeight;
    HFONT hFont;
    HFONT hFontOld;


    // Get dimensions of paper into rect
    rect.left = 0;
    rect.top = 0;
    rect.right =  GetDeviceCaps(pInfo->hDc, HORZRES);
    rect.bottom = GetDeviceCaps(pInfo->hDc, VERTRES);


    nHeight = -MulDiv(36, GetDeviceCaps(pInfo->hDc, LOGPIXELSY), 72);


    // Create Font
    hFont = CreateFont(nHeight, 0, 
        0, 0, FW_DONTCARE, 
        FALSE, FALSE, FALSE, 
        ANSI_CHARSET, 
        OUT_TT_PRECIS, 
        CLIP_DEFAULT_PRECIS,
        PROOF_QUALITY, 
        VARIABLE_PITCH,
        "Arial");
    MTASSERT( hFont != 0);


    // Draw into memory device context
    hDcMem = CreateCompatibleDC(pInfo->hDc);
    hFontOld = SelectObject(hDcMem, hFont);
    iHeight = DrawText(hDcMem, pInfo->szText, -1,  &rect, DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
    rectMem = rect;
    rectMem.left = rect.left + iHeight;
    rectMem.right = rect.right + (iHeight*2);
    bmpMem = CreateCompatibleBitmap(hDcMem,
                                    rectMem.right, rect.bottom);
    bmpOld = SelectObject(hDcMem, bmpMem);
    OffsetRect(&rect, iHeight, 0); 
    DrawText(hDcMem, pInfo->szText, -1,  &rect,
             DT_LEFT | DT_NOPREFIX | DT_WORDBREAK);


    // Italicize bitmap. We use GetPixel and
    // SetPixel because they are horribly inefficient,
    // thereby causing the thread to run for awhile.
    for (y = 0; y < iHeight; y++)
    {   // Italicize line y
        for (x = rectMem.right; x > iHeight; x--)
        {   // Move specified pixel to the right.
            COLORREF color;
            int offset;
            offset = y - iHeight;
            color = GetPixel(hDcMem, x + offset, y);
            if (color != 0)
                counter++;
            SetPixel(hDcMem, x, y, color);
        } // end for x
    } // end for y
    MTASSERT( counter > 0);


    // Copy bitmap of italicized text from memory to device
    if (pInfo->bPrint)
    {
        BitBlt(pInfo->hDc, 50, 50, rectMem.right-rect.left, rectMem.bottom-rect.top,
            hDcMem, iHeight, 0, SRCCOPY);
    }


    SelectObject(hDcMem, hFontOld);
    SelectObject(hDcMem, bmpOld);
    DeleteDC(hDcMem);


    if (!pInfo->bPrint)
    {
        // We can't just write to the global variable where the
        // bitmap is kept or we might overwrite the work of
        // another thread, thereby "losing" a bitmap


        // Also, if we used PostMessage instead of SendMessage, then
        // the rectangle could have been deleted (it's on the stack)
        // by the time the main message loop is reached.
        SendMessage(pInfo->hDlg, WM_SHOWBITMAP, (WPARAM)&rectMem, (LPARAM) bmpMem);
    }


    if (pInfo->bPrint)
    {   // Finish printing
        int result;


        result = EndPage(pInfo->hDc);
        MTASSERT (result != SP_ERROR);
        result = EndDoc(pInfo->hDc);
        MTASSERT (result != SP_ERROR);
        DeleteDC(pInfo->hDc);
        // If we are printing, we are done with the bitmap.
        DeleteObject(bmpMem);
    } 
    else
    {
        ReleaseDC(pInfo->hWndParent, pInfo->hDc);
    }


    // free data structure passed in.
    HeapFree(GetProcessHeap(), 0, pInfo);


    return 0;
}

在这个消息循环中必须注意的几点:
1.在收到WM_QUIT之后,windows仍然会传送消息给你。如果你要在收到WM_QUIT之后等待所有的线程结束,你必须处理你的
消息,否则窗口会变得反应迟钝,没有重绘能力。
2.MsgWaitForMultipleObjects()不允许数组中有缝隙产生。所以当某个handle被激发了时,应该在下一次调用MsgWaitForMultipleObjects()之前把handles数组做一个整理,紧压,不要只是把数组中的


handle设为NULL。
3.如果有另外一个线程更改了对象数组,而那时你正在等待的,那么你需要用一种办法,可以强迫MsgWaitForMultipleObjects()返回,并重新开始,以包含这个新的handle。上面采用WM_THREADCOUNT消


息解决。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值