第二十章 多任务和多线程(多任务的各种模式1)
来源:互联网 发布:田径女神走红网络 编辑:程序博客网 时间:2024/06/05 03:28
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) ;
再论随机矩形
程序20-1 RNDRCTMT是第五章里的RANDRECT程序的多线程版本,您将回忆起RANDRECT使用的是PeekMessage循环来显示一系列的随机矩形。
RNDRCTMT.C
/*---------------------------------------------------------------------------
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) ;
}
在建立多线程的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支持-都是相同的。
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) ;
}
- 第二十章 多任务和多线程(多任务的各种模式1)
- 第二十章 多任务和多线程(多任务的各种模式4)
- 第二十章 多任务和多线程(多任务的各种模式4)
- 第二十章 多任务和多线程(多任务的各种模式2)
- 第二十章 多任务和多线程(多任务的各种模式)
- 第二十章 多任务和多线程
- 第二十章 多任务和多线程(线程同步)
- 多任务和多线程(1)
- 多任务和多线程
- .NET多线程编程(1):多任务和多线程
- .NET多线程编程(1):多任务和多线程
- NET多线程编程(1):多任务和多线程
- .NET多线程编程(1):多任务和多线程
- .NET多线程编程(1):多任务和多线程
- .NET多线程编程(1):多任务和多线程
- .NET多线程编程(1):多任务和多线程
- .NET多线程编程(1):多任务和多线程
- .NET多线程编程(1):多任务和多线程
- 不使用临时表,仅使用select实现查询出多行常数
- 第二十章 多任务和多线程(线程同步)
- 第二十章 多任务和多线程(多任务的各种模式4)
- 第二十章 多任务和多线程(多任务的各种模式4)
- 第二十章 多任务和多线程(多任务的各种模式2)
- 第二十章 多任务和多线程(多任务的各种模式1)
- 第二十章 多任务和多线程(多任务的各种模式)
- 第十七章 文字和字体(有趣的东西)
- 如何删除windows service
- 第十五章 与设备无关的位图(DIB 和 DDB 的结合2)
- 第十五章 与设备无关的位图(DIB 和 DDB 的结合2)
- 第十五章 与设备无关的位图(DIB 和 DDB 的结合2)
- 第十五章 与设备无关的位图(显示和打印3)
- 第十四章 位图和Bitblt(GDI 位图对象1)