如何避免线程浪费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消
息解决。