Windows 的多线程处理

建立新的线程的API函数是CreateThread,它的语法如下:

hThread = CreateThread (&security_attributes, dwStackSize, ThreadProc, pParam, dwFlags, &idThread) ;

第一个参数是指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL。第二个参数是用于新线程的初始堆栈大小,默认值为0。在任何情况下,Windows根据需要动态延长堆栈的大小。
CreateThread的第三个参数是指向线程函数的指标。函数名称没有限制,但是必须以下列形式声明:

DWORD WINAPI ThreadProc (PVOID pParam) ;

CreateThread的第四个参数为传递给ThreadProc的参数。这样主线程和从属线程就可以共享数据。
CreateThread 的 第 五 个 参 数 通 常 为 0 , 但 当 建 立 的 线 程 不 马 上 执 行 时 为 旗 标CREATE_SUSPENDED。线程将暂停直到呼叫ResumeThread来恢复线程的执行为止。
第六个参数是一个指标,指向接受执行绪ID值的变量。
大多数Windows程序写作者喜欢用在PROCESS.H表头文件中声明的C执行时期链接库函数_beginthread。它的语法如下:

hThread = _beginthread (ThreadProc, uiStackSize, pParam) ;

它更简单,对于大多数应用程序很完美,这个线程函数的语法为:

void __cdecl ThreadProc (void * pParam) ;

随机矩形

/*------------------------------------------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, TEXT ("Random Rectangles"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;
}VOID Thread (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 ;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)) ;ReleaseDC (hwnd, hdc) ;DeleteObject (hBrush) ;}}
}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{switch (message){case WM_CREATE:// 新建线程_beginthread (Thread, 0, NULL) ;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) ;
}

程序竞赛

单线程

/*---------------------------------------MULTI1.C -- Multitasking Demo(c) Charles Petzold, 1998---------------------------------------*/#include <windows.h>
#include <math.h>LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;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, TEXT ("Multitasking Demo"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;
}int CheckBottom (HWND hwnd, int cyClient, int iLine)
{if (iLine * cyChar + cyChar > cyClient){InvalidateRect (hwnd, NULL, TRUE) ;UpdateWindow (hwnd) ;iLine = 0 ;}return iLine ;
}// ------------------------------------------------
// Window 1: Display increasing sequence of numbers
// ------------------------------------------------LRESULT APIENTRY WndProc1 (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static int iNum, iLine, cyClient ;HDC        hdc ;TCHAR      szBuffer[16] ;switch (message){case WM_SIZE:cyClient = HIWORD (lParam) ;return 0 ;case WM_TIMER:if (iNum < 0)iNum = 0 ;iLine = CheckBottom (hwnd, cyClient, iLine) ;hdc = GetDC (hwnd) ;TextOut (hdc, 0, iLine * cyChar, szBuffer, wsprintf (szBuffer, TEXT ("%d"), iNum++)) ;ReleaseDC (hwnd, hdc) ;iLine++ ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}// ------------------------------------------------------
// Window 2: Display increasing sequence of prime numbers
// ------------------------------------------------------LRESULT APIENTRY WndProc2 (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static int iNum = 1, iLine, cyClient ;HDC        hdc ;int        i, iSqrt ;TCHAR      szBuffer[16] ;switch (message){case WM_SIZE:cyClient = HIWORD (lParam) ;return 0 ;case WM_TIMER:do   {if (++iNum < 0)iNum = 0 ;iSqrt = (int) sqrt (iNum) ;for (i = 2 ; i <= iSqrt ; i++)if (iNum % i == 0)break ;}while (i <= iSqrt) ;iLine = CheckBottom (hwnd, cyClient, iLine) ;hdc = GetDC (hwnd) ;TextOut (hdc, 0, iLine * cyChar, szBuffer, wsprintf (szBuffer, TEXT ("%d"), iNum)) ;ReleaseDC (hwnd, hdc) ;iLine++ ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}// ----------------------------------------------------------
// Window 3: Display increasing sequence of Fibonacci numbers
// ----------------------------------------------------------LRESULT APIENTRY WndProc3 (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static int iNum = 0, iNext = 1, iLine, cyClient ;HDC        hdc ;int        iTemp ;TCHAR      szBuffer[16] ;switch (message){case WM_SIZE:cyClient = HIWORD (lParam) ;return 0 ;case WM_TIMER:if (iNum < 0){iNum  = 0 ;iNext = 1 ;}iLine = CheckBottom (hwnd, cyClient, iLine) ;hdc = GetDC (hwnd) ;TextOut (hdc, 0, iLine * cyChar, szBuffer, wsprintf (szBuffer, TEXT ("%d"), iNum)) ;ReleaseDC (hwnd, hdc) ;iTemp  = iNum ;iNum   = iNext ;iNext += iTemp ;iLine++ ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}// -----------------------------------------
// Window 4: Display circles of random radii
// -----------------------------------------LRESULT APIENTRY WndProc4 (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static int cxClient, cyClient ;HDC        hdc ;int        iDiameter ;switch (message){case WM_SIZE:cxClient = LOWORD (lParam) ;cyClient = HIWORD (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) ;
}// -----------------------------------
// Main window to create child windows
// -----------------------------------LRESULT APIENTRY WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static HWND    hwndChild[4] ;static TCHAR * szChildClass[] = { TEXT ("Child1"), TEXT ("Child2"),TEXT ("Child3"), TEXT ("Child4") } ;static WNDPROC ChildProc[] = { WndProc1, WndProc2, WndProc3, WndProc4 } ;HINSTANCE      hInstance ;int            i, cxClient, cyClient ;WNDCLASS       wndclass ;switch (message){case WM_CREATE:hInstance = (HINSTANCE) GetWindowLong (hwnd, GWL_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:for (i = 0 ; i < 4 ; i++)SendMessage (hwndChild[i], WM_TIMER, wParam, lParam) ;return 0 ;case WM_CHAR:if (wParam == '\x1B')DestroyWindow (hwnd) ;return 0 ;case WM_DESTROY:KillTimer (hwnd, 1) ;PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}

多线程

/*---------------------------------------MULTI2.C -- Multitasking Demo(c) Charles Petzold, 1998---------------------------------------*/#include <windows.h>
#include <math.h>
#include <process.h>typedef struct
{HWND hwnd ;int  cxClient ;int  cyClient ;int  cyChar ;BOOL bKill ; // 是否终止
}
PARAMS, *PPARAMS ;LRESULT APIENTRY WndProc (HWND, UINT, WPARAM, LPARAM) ;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, TEXT ("Multitasking Demo"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;
}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 ;
}// ------------------------------------------------
// Window 1: Display increasing sequence of numbers
// ------------------------------------------------void Thread1 (PVOID pvoid)
{HDC     hdc ;int     iNum = 0, iLine = 0 ;PPARAMS pparams ;TCHAR   szBuffer[16] ;pparams = (PPARAMS) pvoid ;while (!pparams->bKill){if (iNum < 0)iNum = 0 ;iLine = CheckBottom (pparams->hwnd,   pparams->cyClient,pparams->cyChar, iLine) ;hdc = GetDC (pparams->hwnd) ;TextOut (hdc, 0, iLine * pparams->cyChar, szBuffer, wsprintf (szBuffer, TEXT ("%d"), iNum++)) ;               ReleaseDC (pparams->hwnd, hdc) ;iLine++ ;}_endthread () ;
}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 ()) ;_beginthread (Thread1, 0, &params) ;return 0 ;case WM_SIZE:params.cyClient = HIWORD (lParam) ;return 0 ;case WM_DESTROY:params.bKill = TRUE ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}// ------------------------------------------------------
// Window 2: Display increasing sequence of prime numbers
// ------------------------------------------------------void Thread2 (PVOID pvoid)
{HDC     hdc ;int     iNum = 1, iLine = 0, i, iSqrt ;PPARAMS pparams ;TCHAR   szBuffer[16] ;pparams = (PPARAMS) pvoid ;while (!pparams->bKill){do{if (++iNum < 0)iNum = 0 ;iSqrt = (int) sqrt (iNum) ;for (i = 2 ; i <= iSqrt ; i++)if (iNum % i == 0)break ;}while (i <= iSqrt) ;iLine = CheckBottom (pparams->hwnd,   pparams->cyClient,pparams->cyChar, iLine) ;hdc = GetDC (pparams->hwnd) ;TextOut (hdc, 0, iLine * pparams->cyChar, szBuffer, wsprintf (szBuffer, TEXT ("%d"), iNum)) ;ReleaseDC (pparams->hwnd, hdc) ;iLine++ ;}_endthread () ;
}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是指向PARAMS结构的指针,允许原来的线程和新线程共享信息,而不必借助于整体变量// PARAMS的_beginthread (Thread2, 0, &params) ;return 0 ;case WM_SIZE:params.cyClient = HIWORD (lParam) ;return 0 ;case WM_DESTROY:params.bKill = TRUE ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}// Window 3: Display increasing sequence of Fibonacci numbers
// ----------------------------------------------------------void Thread3 (PVOID pvoid)
{HDC     hdc ;int     iNum = 0, iNext = 1, iLine = 0, iTemp ;PPARAMS pparams ;TCHAR   szBuffer[16] ;pparams = (PPARAMS) pvoid ;while (!pparams->bKill){if (iNum < 0){iNum  = 0 ;iNext = 1 ;}iLine = CheckBottom (pparams->hwnd,   pparams->cyClient,pparams->cyChar, iLine) ;hdc = GetDC (pparams->hwnd) ;TextOut (hdc, 0, iLine * pparams->cyChar, szBuffer, wsprintf (szBuffer, TEXT ("%d"), iNum)) ;ReleaseDC (pparams->hwnd, hdc) ;iTemp  = iNum ;iNum   = iNext ;iNext += iTemp ;iLine++ ;}_endthread () ;
}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 ()) ;_beginthread (Thread3, 0, &params) ;return 0 ;case WM_SIZE:params.cyClient = HIWORD (lParam) ;return 0 ;case WM_DESTROY:params.bKill = TRUE ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}// -----------------------------------------
// Window 4: Display circles of random radii
// -----------------------------------------void Thread4 (PVOID pvoid)
{HDC     hdc ;int     iDiameter ;PPARAMS pparams ;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 () ;
}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 ()) ;_beginthread (Thread4, 0, &params) ;return 0 ;case WM_SIZE:params.cxClient = LOWORD (lParam) ;params.cyClient = HIWORD (lParam) ;return 0 ;case WM_DESTROY:params.bKill = TRUE ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}// -----------------------------------
// Main window to create child windows
// -----------------------------------LRESULT APIENTRY WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static HWND    hwndChild[4] ;static TCHAR * szChildClass[] = { TEXT ("Child1"), TEXT ("Child2"),TEXT ("Child3"), TEXT ("Child4") } ;static WNDPROC ChildProc[] = { WndProc1, WndProc2, WndProc3, WndProc4 } ;HINSTANCE      hInstance ;int            i, cxClient, cyClient ;WNDCLASS       wndclass ;switch (message){case WM_CREATE:hInstance = (HINSTANCE) GetWindowLong (hwnd, GWL_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')DestroyWindow (hwnd) ;return 0 ;case WM_DESTROY:PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}

临界区域

// 声明
CRITICAL_SECTION cs ;
// 初始化
InitializeCriticalSection (&cs) ;
// 现成使用临界区域
EnterCriticalSection (&cs) ;
// 线程离开
LeaveCriticalSection (&cs) ;
// 删除
DeleteCriticalSection (&cs) ;

一个线程可以进入一个临界区域,设定一个结构的字段,然后退出临界区域。另一个使用该结构的线程在存取结构中的字段之前也要先进入该临界区域,然后再退出临界区域。

临界区域的一个限制是它们只能用于在同一程序内的线程之间的协调。但是在某些情况下,您需要协调两个不同程序对同一资源的共享(如共享内存等)。在此其况下不能使用临界区域,但是可以使用一种被称为「互斥对象(mutex object)」的技术。「mutex」是个合成字,代表「mutual exclusion(互斥)」,它在这里精确地表达了我们的目的。我们想防止一个程序的线程在更新数据或者使用共享内存与其它资源时被中断。

事件信号

/*----------------------------------------BIGJOB1.C -- Multithreading Demo(c) Charles Petzold, 1998----------------------------------------*/#include <windows.h>
#include <math.h>
#include <process.h>#define REP              10000000 //Translator: the original value 1000000 is too small, increase 10 times to be 10000000#define STATUS_READY     0
#define STATUS_WORKING   1
#define STATUS_DONE      2#define WM_CALC_DONE     (WM_USER + 0)
#define WM_CALC_ABORTED  (WM_USER + 1)typedef struct
{HWND hwnd ;  // 程序窗口的句柄BOOL bContinue ;  // 指示线程是否继续计算或者停止
}
PARAMS, *PPARAMS ;LRESULT APIENTRY WndProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static TCHAR szAppName[] = TEXT ("BigJob1") ;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, TEXT ("Multithreading Demo"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;
}void Thread (PVOID pvoid)
{double           A = 1.0 ;INT              i ;LONG             lTime ;// volatile限定字向编译器指出变量可能会在实际的程序叙述外被修改volatile PPARAMS pparams ;pparams = (PPARAMS) pvoid ;// 取得以毫秒计的Windows启动以来已经执行了的时间lTime = GetCurrentTime () ;for (i = 0 ; i < REP && pparams->bContinue ; i++)A = tan (atan (exp (log (sqrt (A * A))))) + 1.0 ;if (i == REP){lTime = GetCurrentTime () - lTime ;SendMessage (pparams->hwnd, WM_CALC_DONE, 0, lTime) ;}elseSendMessage (pparams->hwnd, WM_CALC_ABORTED, 0, 0) ;_endthread () ;
}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{// 表示程序是否准备好进行一次计算,是否正在进行一次计算,或者是否完成了计算static INT     iStatus ;static LONG    lTime ;static PARAMS  params ;static TCHAR * szMessage[] = { TEXT ("Ready (left mouse button begins)"),TEXT ("Working (right mouse button ends)"),TEXT ("%d repetitions in %ld msec") } ;HDC            hdc ;PAINTSTRUCT    ps ;RECT           rect ;TCHAR          szBuffer[64] ;switch (message){case WM_LBUTTONDOWN:if (iStatus == STATUS_WORKING){MessageBeep (0) ;return 0 ;}iStatus = STATUS_WORKING ;params.hwnd = hwnd ;params.bContinue = TRUE ;_beginthread (Thread, 0, &params) ;InvalidateRect (hwnd, NULL, TRUE) ;return 0 ;case WM_RBUTTONDOWN:params.bContinue = FALSE ;return 0 ;case WM_CALC_DONE:lTime = lParam ;iStatus = STATUS_DONE ;InvalidateRect (hwnd, NULL, TRUE) ;return 0 ;case WM_CALC_ABORTED:iStatus = STATUS_READY ;InvalidateRect (hwnd, NULL, TRUE) ;return 0 ;case WM_PAINT:hdc = BeginPaint (hwnd, &ps) ;GetClientRect (hwnd, &rect) ;wsprintf (szBuffer, szMessage[iStatus], REP, lTime) ;DrawText (hdc, szBuffer, -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;EndPaint (hwnd, &ps) ;return 0 ;case WM_DESTROY:PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}

事件对象

上面程序在每次需要执行暴力测试计算时,就建立一个执行绪。执行绪在完成计算之后自动终止。
另一种可用的方法是在程序的整个生命周期内保持线程的执行,但是只在必要时才启动它。这是一个应用事件对象的理想情况。
事件对象可以是「有信号的」(也称为「被设立的」)或「没信号的」(也称为「被重置的」)。
您可以通过下面呼叫来建立事件对象:

hEvent = CreateEvent (&sa, fManual, fInitial, pszName) ;

第一个参数(指向一个SECURITY_ATTRIBUTES结构的指针)和最后一个参数(一个事件对象的名字)只有在事件对象被多个程序共享时才有意义。在同一程序中,这些参数通常被设定为NULL。如果您希望事件对象被初始化为有信号的,那么将fInitial参数设定为TRUE。而如果希望事件对象被初始化为无信号的,则将fInitial参数设定为FALSE。稍后,我将简短地描述fManual参数。
要设立一个现存的事件对象,调用

SetEvent (hEvent) ;

要重置一个事件对象,调用

ResetEvent (hEvent) ;

一个程序通常调用:

WaitForSingleObject (hEvent, dwTimeOut) ;

并且将第二个参数设定为INFINITE。如果事件对象目前是被设立的,那么函数将立即传回,否则,函数将暂停线程直到事件对象被设立。如果您将第二个参数设定为一个以毫秒计的超时时间值,这样函数也可能在事件对象被设立之前传回。
如果最初的CreateEvent呼叫的fManual参数被设定为FALSE,那么事件对象将在WaitForSingleObject函数传回时自动重置。这种功能特性通常使得事件对象没有必要使用ResetEvent函数。

 /*----------------------------------------BIGJOB2.C -- Multithreading Demo(c) Charles Petzold, 1998----------------------------------------*/#include <windows.h>
#include <math.h>
#include <process.h>#define REP              10000000 //Translator: the original value 1000000 is too small, increase 10 times to be 10000000#define STATUS_READY     0
#define STATUS_WORKING   1
#define STATUS_DONE      2#define WM_CALC_DONE     (WM_USER + 0)
#define WM_CALC_ABORTED  (WM_USER + 1)typedef struct
{HWND   hwnd ;HANDLE hEvent ;BOOL   bContinue ;
}
PARAMS, *PPARAMS ;LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static TCHAR szAppName[] = TEXT ("BigJob2") ;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, TEXT ("Multithreading Demo"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;
}void Thread (PVOID pvoid)
{double           A = 1.0 ;INT              i ;LONG             lTime ;volatile PPARAMS pparams ;pparams = (PPARAMS) pvoid ;// 无限循环while (TRUE){// 事件被初始化为重置的// pparams包含包含事件对象句柄的字段WaitForSingleObject (pparams->hEvent, INFINITE) ;lTime = GetCurrentTime () ;for (i = 0 ; i < REP && pparams->bContinue ; i++)A = tan (atan (exp (log (sqrt (A * A))))) + 1.0 ;if (i == REP){lTime = GetCurrentTime () - lTime ;PostMessage (pparams->hwnd, WM_CALC_DONE, 0, lTime) ;}elsePostMessage (pparams->hwnd, WM_CALC_ABORTED, 0, 0) ;}
}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static HANDLE  hEvent ;static INT     iStatus ;static LONG    lTime ;static PARAMS  params ;static TCHAR * szMessage[] = { TEXT ("Ready (left mouse button begins)"),TEXT ("Working (right mouse button ends)"),TEXT ("%d repetitions in %ld msec") } ;HDC            hdc ;PAINTSTRUCT    ps ;RECT           rect ;TCHAR          szBuffer[64] ;switch (message){case WM_CREATE:// 建立一个初始化为没信号的自动重置事件对象hEvent = CreateEvent (NULL, FALSE, FALSE, NULL) ;params.hwnd = hwnd ;params.hEvent = hEvent ;params.bContinue = FALSE ;// 建立线程_beginthread (Thread, 0, &params) ;return 0 ;case WM_LBUTTONDOWN:if (iStatus == STATUS_WORKING){MessageBeep (0) ;return 0 ;}iStatus = STATUS_WORKING ;params.bContinue = TRUE ;// 设立一个现存的事件对象SetEvent (hEvent) ;InvalidateRect (hwnd, NULL, TRUE) ;return 0 ;case WM_RBUTTONDOWN:params.bContinue = FALSE ;return 0 ;case WM_CALC_DONE:lTime = lParam ;iStatus = STATUS_DONE ;InvalidateRect (hwnd, NULL, TRUE) ;return 0 ;case WM_CALC_ABORTED:iStatus = STATUS_READY ;InvalidateRect (hwnd, NULL, TRUE) ;return 0 ;case WM_PAINT:hdc = BeginPaint (hwnd, &ps) ;GetClientRect (hwnd, &rect) ;wsprintf (szBuffer, szMessage[iStatus], REP, lTime) ;DrawText (hdc, szBuffer, -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;EndPaint (hwnd, &ps) ;return 0 ;case WM_DESTROY:PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;
}

线程区域储存空间 (TLS)

多线程程序中的整体变量(以及任何被配置的内存)被程序中的所有线程共享。在一个函数中的局部静态变量也被使用函数的所有线程共享。一个函数中的局部动态变量是唯一于各个线程的,因为它们被储存在堆栈上,而每个线程有它自己的堆栈。
对各个线程唯一的持续性储存空间有存在的必要。例如,我在本章前面提到过的C中的strtok函数要求这种型态的储存空间。不幸的是,C语言不支持这类储存空间。但是Windows中提供了四个函数,它们实作了一种技术来做到这一点,并且Microsoft对C的扩充语法也支持它,这就叫做线程区域储存空间。
下面是API工作的方法:
首先,定义一个包含需要唯一于线程的所有数据的结构,例如:

typedef struct
{
int a ;
int b ;
}
DATA, * PDATA ;

主线程呼叫TlsAlloc获得一个索引值:

dwTlsIndex = TlsAlloc () ;

这个值可以储存在一个整体变量中或者通过参数结构传递给线程函数。
线程函数首先为该数据结构配置内存,并使用上面所获得的索引值呼叫TlsSetValue:

TlsSetValue (dwTlsIndex, GlobalAlloc (GPTR, sizeof (DATA)) ;

该函数将一个指标和某个线程及某个线程索引相关联。现在,任何需要使用这个指标的函数(包括最初的线程函数本身)都可以包含如下所示的程序代码:

PDATA pdata ;
...
pdata = (PDATA) TlsGetValue (dwTlsIndex) ;

现在函数可以设定或者使用pdata->a和pdata->b了。在线程函数终止以前,它释放配置的内存:

GlobalFree (TlsGetValue (dwTlsIndex)) ;

当使用该数据的所有线程都终止之时,主线程将释放索引:

TlsFree (dwTlsIndex) ;

这个程序刚开始可能令人有些迷惑,因此如果能看一看如何实作线程区域储存空间可能会有帮助(我不知道Windows实际上是如何实作的,但下面的方案是可能的)。首先,TlsAlloc可能只是配置一块内存(长度为0)并传回一个索引值,即指向这块内存的一个指针。每次使用该索引呼叫TlsSetValue时,通过重新配置将内存块增大8个字节。在这8个字节中储存的是呼叫函数的线程ID(通过GetCurrentThreadId来获得)以及传递给TlsSetValue函数的指标。TlsSetValue简单地使用线程ID来搜寻操作系统管理的线程区域储存空间地址表,然后传回指标。TlsFree将释放内存块。所以您看,这可能是一件容易得可以由您自己来实作的事情。不过,既然已经有工具为您做好了这些工作,那也不错。
Microsoft对C的扩充功能使这件工作更加容易。只要在要对每个线程都保留不同内容的变量前加上__declspec (thread)就好了。对于任何函数的外部静态变量,则为:

__declspec (thread) int iGlobal = 1 ;

对于函数内部的静态变量,则为:

__declspec (thread) static int iLocal = 2 ;

Windows程序设计-多任务和多线程相关推荐

  1. 《Windows程序设计》读书笔二十 多任务和多线程

    多任务是指操作系统能够并行运行多个程序的能力. 多线程是指一个应用程序在其自身内部也有支持多任务的能力. 20.1 多任务的模型 20.1.1 DOS下的多任务 早期DOS使用终止并驻留(Termin ...

  2. Windows程序设计“圣经”

    Windows程序设计"圣经" 十年依旧畅销不衰的神品 传奇大师带你走入Windows编程圣殿 "Windows程序设计最举足轻重的书当然就是Charles Petzol ...

  3. Win32程序设计(Windows程序设计机理)

    Win32程序设计,又常常被叫做SDK/API编程,现在使用Win32的程序员确实已经很少了,主要是因为用win32写代码比用vc写起来还累,但是Win32编程常常又是非常有效率的,现在用的比较多的就 ...

  4. windows程序设计相关思想

    就我个人观点,C++/MFC 程序设计必须跨越四大技术障碍: 1. 对象导向观念与C++ 语言. 2. Windows 程序基本观念(程序进入点.消息流动.窗口函数.callback...). 3.  ...

  5. createprocess重启程序_C++_VC程序设计中CreateProcess用法注意事项,对于windows程序设计来说,启动 - phpStudy...

    VC程序设计中CreateProcess用法注意事项 对于windows程序设计来说,启动一个进程有三种方法:WinExec,ShellExecute,CreateProcess.这里仅对Create ...

  6. Windows程序设计最新书籍教程

    <深入浅出Windows API程序设计:编程基础篇> 出版社:人民邮电出版社 书号:978-7-115-56948-6 出版时间:2022-05-01 ◆ 基础篇的内容是学习Window ...

  7. windows程序设计(第5版 珍藏版)学习笔记

    第1章 起步 windows简单程序示例及说明 #include <windows.h>//包含其他windows头文件int WINAPI WinMain (HINSTANCE hIns ...

  8. Windows程序设计学习笔记(1):一个简单的windows程序

    <Windows程序设计>(第五版)(美Charles Petzold著) 1 #include<windows.h> 2 3 LRESULT CALLBACK WndProc ...

  9. Windows程序设计------字体不等宽引出的问题及其细节知识

    在写Windows程序设计的Typer程序时,我并不是在每一个使用HDC的地方都重新创建选中字体,而是在一开始选中之后,就没有再删除它,代码如图: 结果我的字体不是等宽字体! 起先我以为是没有设置WM ...

最新文章

  1. 【吐血整理】java正则表达式详解
  2. 红黑树二叉查找树二叉排序树的理解
  3. 计算机专业技能知识,2017年度计算机专业技能知识资料基础知识资料试题'及其答案...
  4. oracle cpu 利用率过高 kswapd0_服务器带宽监测与利用率过高的解决办法
  5. 2021博客之星,请帮忙投上宝贵一票
  6. python selenium po模式_Python+Selenium+Unittest实现PO模式web自动化框架
  7. 东北大哥在线反套路hhhhhh | 今日最佳
  8. 质量效能研发部php,【百度】百度质量效能研发部【社招/实习】
  9. 浅谈 OneAPM 在 express 项目中的实践
  10. getFields和getDeclaredFields
  11. GitHub 使用教程图文详解(转)
  12. 如何选择合适的数据可视化BI工具
  13. WPF视频教程系列笔记
  14. 南京林业大学计算机考研资料,2021南京林业大学考研历年真题复习资料
  15. 智和网管平台SugarNMS网络安全解决方案
  16. gis 数据框裁剪_【更新89篇】地理数据科学技术文章合集,欢迎大家点赞、在看、转发三连!...
  17. HTML+CSS+JAVASCRIPT 高仿低配网页版网易云音乐播放器
  18. JUL、JCL、Log4j、Slf4j各种日志框架的使用
  19. mysql ix锁_mysql锁详解
  20. [分块]Most Influential Pumpkin

热门文章

  1. win7下安装ubuntu
  2. AI 绘画用 Stable Diffusion 图生图局部重绘功能给美女换装(这是我能看的嘛)
  3. 为什么java以类为基本单位
  4. 最近很燥,决心沉下心来学习!
  5. 《论文阅读》Variational Relational Point Completion Network
  6. javaWeb开发面试题目
  7. RuntimeError: Calculated padded input size per channel: (1 x 1). Kernel size: (5 x 5). Kernel size c
  8. C++ 实现FTP上传文件
  9. LoadLibrary() 错误码问题 14001, 126
  10. cidr php,PHP怎么实现ip2cidr(生成多个cidr)