windows程序设计(18):多线程程序设计

来源:互联网 发布:奇兵网络小程序 编辑:程序博客网 时间:2024/06/05 07:33

先得多说两句进程与线程的基本知识。不过自己不是学计算机的,看《操作系统》的感觉就跟看马哲差不多,全是原理性的内容,感觉很干涩,就勉强说几句吧。

进程是程序,数据以及进程控制块(PCB)组成的,它是资源分配的最小单位。为什么要提出线程的概念呢?因为进程是资源的拥有者,所以进程使用起来代价太大了,得创建,撤销,切换,而且进程间数据的交换必须使用特定的机制。这导致一个系统中同时存在的进程不宜过多。由此,提出了一个比进程更小的概念——线程。线程是资源调度的最小单位,占有的资源少,而且可以共享进程资源。这意味着多个线程可以共享数据等资源,这就避免了复杂的通信机制。总而言之,线程的速度比进程的快。

肤浅的理解就是这些吧。还是看程序。

程序的要求是这样的:

显示4个窗口。第一个窗口必须显示一系列的递增数,第二个必须显示一系列的递增质数,而第三个必须显示Fibonacci数列(Fibonacci数列以数字0和1开始,后头每一个数都是其前两个数的和-即0、1、1、2、3、5、8等等)。这三个窗口应该在数字达到窗口底部时或者进行滚动,或者自行清除窗口内容。第四个窗口必须显示任意半径的圆,而程序必须在按下一个Escape键时终止。

先看一个非多线程的版本:

/*---------------------------------------   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 TCHARszAppName[] = TEXT ("Multi1") ;HWNDhwnd ;MSGmsg ;WNDCLASSwndclass ;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) ;//将行宽置为0iLine = 0 ;}return iLine ;}//窗口1:展示递增的序列LRESULT APIENTRY WndProc1 (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static intiNum, iLine, cyClient ;HDChdc ;TCHARszBuffer[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) ;}//窗口2:显示递增的质数//求素数的方法:对于N来说,不必用从2到N一1的所有素数去除,用小于等于√N(根号N)的所有素数去除就可以了LRESULT APIENTRY WndProc2 (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static intiNum = 1, iLine, cyClient ;HDChdc ;inti, iSqrt ;TCHARszBuffer[16] ;switch (message){case WM_SIZE:cyClient = HIWORD (lParam) ;return 0 ;case WM_TIMER:do   {if (++iNum < 0)iNum = 0 ;//获得该数的开方iSqrt = (int) sqrt ((double)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) ;}//窗口3:显示Fibonacci数列LRESULT APIENTRY WndProc3 (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static intiNum = 0, iNext = 1, iLine, cyClient ;HDChdc ;intiTemp ;TCHARszBuffer[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) ;//F0=0,F1=1,Fn=F(n-1)+F(n-2)iTemp  = iNum ;iNum   = iNext ;iNext += iTemp ;iLine++ ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}//窗口4:显示半径随机的圆LRESULT APIENTRY WndProc4 (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static intcxClient, cyClient ;HDChdc ;intiDiameter ;switch (message){case WM_SIZE://或得客户区大小cxClient = LOWORD (lParam) ;cyClient = HIWORD (lParam) ;return 0 ;case WM_TIMER:InvalidateRect (hwnd, NULL, TRUE) ;UpdateWindow (hwnd) ;//确保直径最小为1,最大不会超过客户区的显示范围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) ;}//主窗口程序:LRESULT APIENTRY WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static HWNDhwndChild[4] ;static TCHAR *szChildClass[] = { TEXT ("Child1"), TEXT ("Child2"),    TEXT ("Child3"), TEXT ("Child4") } ;static WNDPROCChildProc[] = { WndProc1, WndProc2, WndProc3, WndProc4 } ;HINSTANCEhInstance ;inti, cxClient, cyClient ;WNDCLASSwndclass ;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://16进制1B即10进制27:ESC键的ASC码if (wParam == '\x1B')DestroyWindow (hwnd) ;return 0 ;case WM_DESTROY:KillTimer (hwnd, 1) ;PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}


 

程序流程比较简单:

在主窗口的回调函数的WM_CREATE消息下,创建并注册4个子窗口,然后设置一个计时器;在WM_SIZE消息下将它们移动到正确的位置上;每当WM_TIMER到来,向4个子窗口发送时钟信号。对于每个子窗口,当接收到WM_TIMER消息时,完成自己工作。

再看一个多线程的版本:

/*---------------------------------------   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 TCHARszAppName[] = TEXT ("Multi2") ;HWNDhwnd ;MSGmsg ;WNDCLASSwndclass ;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 ;}//窗口1:展示递增的序列void Thread1 (PVOID pvoid){HDChdc ;intiNum = 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, ¶ms) ;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) ;}//窗口2:显示递增的质数void Thread2 (PVOID pvoid){HDChdc ;intiNum = 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 ((double)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 ()) ;_beginthread (Thread2, 0, ¶ms) ;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) ;}//窗口3:显示Fibonacci数列void Thread3 (PVOID pvoid){HDChdc ;intiNum = 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, ¶ms) ;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) ;}//窗口4:画半径随机的椭圆void Thread4 (PVOID pvoid){HDChdc ;intiDiameter ;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, ¶ms) ;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) ;}//主窗口的回调函数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) ;}


 

多线程程序的架构是这样的:你的主执行绪建立您程序所需要的所有窗口,并在其中包含所有的窗口消息处理程序,以便处理这些窗口的所有消息;所有其它执行绪只进行一些背景处理,除了和主执行绪通讯,它们不和使用者进行交流。

可以把这种架构想象成:主线程处理使用者输入(和其它消息),并建立程序中的其它线程,这些附加的线程完成与使用者无关的工作。

两个程序区别不大。主要在于WM_CREATE消息处理期间呼叫_beginthread函数来建立另一个线程。

注意:

注意如果你使用的是较老的编译器,比如VC++6.0,需要在「Project Settings」对话框中做一些修改。选择「C/C++」页面标签,然后在「Category」下拉式清单方块中选择「Code Generation」。在「Use Run-Time Library」下拉式清单方块中,可以看到用于「Release」设定的「Single-Threaded」和用于Debug设定的「Debug Single-Threaded」。将这些分别改为「Multithreaded」和「Debug Multithreaded」。但是如果使用的是VS2010,这些改动就不是必须的了。

我们可以看到第二个程序的运行速度明显比第一个快很多。从我的电脑的CPU使用率也可以发现,第一个程序的使用率只有10%左右,第二个达到了80%。

虽然多线程程序运行起来速度很快,但是很多人还是不建议使用它,因为有些时候,会出现线程的切换会产生很严重但又难以察觉的后果,比如线程间必须同步,这需要很多保护机制来避免它们。

 

原创粉丝点击