📄 20. 多任务和多线程.txt
字号:
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) ;
}
在建立多线程的Windows程序时,需要在「Project Settings」对话框中做一些修改。选择「C/C++」页面标签,然后在「Category」下拉式清单方块中选择「Code Generation」。在「Use Run-Time Library」下拉式清单方块中,可以看到用于「Release」设定的「Single-Threaded」和用于Debug设定的「Debug Single-Threaded」。将这些分别改为「Multithreaded」和「Debug Multithreaded」。这将把编译器旗标改为/MT,它是编译器在编译多线程的应用程序所需要的。具体地说,编译器将在.OBJ文件中插入LIBCMT.LIB文件名,而不是LIBC.LIB。连结程序使用这个名称与执行期链接库函数连结。
LIBC.LIB和LIBCMT.LIB文件包含C语言链接库函数,有些C语言链接库函数包含静态数据。例如,由于strtok函数可能被连续地多次呼叫,所以它在静态内存中储存了一个指标。在多线程程序中,每个线程必须在strtok函数中有它自己的静态指针。因此,这个函数的多线程版本稍微不同于单线程的strtok函数。
同时请注意,我在RNDRCTMT.C中包含了表头文件PROCESS.H,这个文件定义一个名为_beginthread的函数,它启动一个新的线程。只有定义了_MT标识符,才会声明这个函数,这是/MT旗标的另一个结果。
在RNDRCTMT.C的WinMain函数中,由CreateWindow传回的hwnd值被储存在一个整体变量中,因此cxClient和cyClient值也可以由窗口消息处理程序的WM_SIZE消息获得。
窗口消息处理程序以最容易的方法呼叫_beginthread-简单地以线程函数的地址(称为Thread)作为第一个参数,其它参数使用0,线程函数传回VOID并有一个参数,该参数是一个指向VOID的指标。在RNDRCTMT中的Thread函数不使用这个参数。
在呼叫了_beginthread函数之后,线程函数(以及该线程函数可能呼叫的其它任何函数)中的程序代码和程序中的其它程序代码同时执行。两个或者多个执行绪使用一个程序中的同一函数,在这种情况下,动态区域变量(储存在堆栈上)对每个执行绪是唯一的。对程序中的所有执行绪来说,所有的静态变量都是一样的。这就是窗口消息处理程序设定整体的cxClient和cyClient变量并由Thread函数使用的方式。
有时您需要唯一于各个线程的持续储存性数据。通常,这种数据是静态变量,但在Windows 98中,您可以使用「线程区域储存空间」,我将在本章后面进行讨论。
程序设计竞赛的问题
1986年10月3日,Microsoft举行了为期一天,针对计算机杂志出版社的技术编辑和作者的简短的记者招待会,来讨论他们当时的一组语言产品,包括他们的第一个交谈式开发环境,QuickBASIC 2.0。当时,Windows 1.0出现还不到一年,但是没有人知道我们什么时候能得到与该环境类似的东西(这花了好几年)。这一事件与众不同的部分原因是由于Microsoft的公关人员所举办的「Storm the Gates」程序设计竞赛。Bill Gates使用QuickBASIC 2.0,而计算机出版社的人员可以使用他们选择的任何语言产品。
竞赛的问题是从公众提出的题目中挑选出来的(挑选那些需要写大约半小时程序来解决的问题),问题如下:
建立一个包含四个窗口的多任务仿真程序。第一个窗口必须显示一系列的递增数,第二个必须显示一系列的递增质数,而第三个必须显示Fibonacci数列(Fibonacci数列以数字0和1开始,后头每一个数都是其前两个数的和-即0、1、1、2、3、5、8等等)。这三个窗口应该在数字达到窗口底部时或者进行滚动,或者自行清除窗口内容。第四个窗口必须显示任意半径的圆,而程序必须在按下一个Escape键时终止。
当然,在1986年10月,在DOS下执行的这样一个程序最多只能是模拟多任务而已,而且没有一个竞赛者具有足够的勇气-并且其中大多数也没有足够的知识-来为Windows编写这个程序。再者,如果真要这么做,当然不会只花半小时了!
参加这次竞赛的大多数人编写了一个程序来将屏幕分为四个区域,程序中包含一个循环,依次更新每个窗口,然后检查是否按下了Escape键。如同DOS环境下的传统习惯,程序占用了百分之百的CPU处理时间。
如果在Windows 1.0中写程序,那么结果将是类似程序20-2 MULTI1的结果。我说「类似」,是因为我编写的程序是32位的,但程序结构和相当多的程序代码-除了变量和函数参数定义以及Unicode支持-都是相同的。
程序20-2 MULTI1
MULTI1.C
/*--------------------------------------------------------------------------
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 ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -