C语言实例教程(PDF格式)-第80部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
参数意义如下:
pfnThreadProc:指向工人线程的控制函数的指针,它不能是NULL。
此控制函数必须声明成如下样式:
UINT MyControllingFunction( LPVOID pParam );
pThreadClass:从CWinThread派生的RUNTIME_CLASS
pParam:传递给工人线程的控制函数的参数
nPriority:线程的期望的优先权。如果这个值为0,则新线程和创建
线程具有同样的优先级。
nStackSize:以字节为单位定义了新线程的堆栈大小。如果这个值为
0,则新线程和创建线程具有同样大小的堆栈。
dwCreateFlags:控制线程创建的附加标志。这个值可以是以下两个
值中的一个:CREATE_SUSPENDED和0。如果是标志是前者,以挂起数
为1启动线程。只有在ResumeThread被调用时,这个线程才会被执
行。如果标志为0,则在创建线程后立即执行线程。
lpSecurityAttrs:指向定义了线程安全属性的SECURITY_ATTRIBUTES
结构的指针。如果为NULL,则新线程和创建线程具有同样的安全属
性。
线程可能具有下面的优先级别:
THREAD_PRIORITY_ABOVE_NORMAL 比正常优先级高一个级别
THREAD_PRIORITY_BELOW_NORMAL 比正常优先级低一个级别
THREAD_PRIORITY_HIGHEST 比正常优先级高两个级别
THREAD_PRIORITY_IDLE 基本优先级为1。对于
REALTIME_PRIORITY_CLASS进程,优先级为16。
…………………………………………………………Page 640……………………………………………………………
THREAD_PRIORITY_LOWEST 比正常优先级低两个级别
THREAD_PRIORITY_NORMAL 正常优先级别
THREAD_PRIORITY_TIME_CRITICAL 基本优先级为15。对于
REALTIME_PRIORITY_CLASS进程,优先级别是30。
一个线程的优先级决定了相对于其它正在运行的线程这个线程控制系
统的时间。通常,线程的级别越高,它的运行时间也越长,这也正是
THREAD_PRIORITY_TIME_CRITICAL如此高的原因。
下面用一个简单的例子说明如何创建线程,按照下面的步骤进行:
使用MFC AppWizard生成一个单文档应用程序Thread。
使用资源编辑器编辑器给程序的IDR_MAINFRAME菜单添加一个菜单
“线程”。
在 “线程”菜单中添加一个菜单项启动线程,其ID为
ID_STARTTHREAD。
在CThreadView类中添加消息映射函数OnStartthread()。
在OnStartthread()函数中添加如下代码:
void CThreadView::OnStartthread()
{
// TODO: Add your mand handler code here
HWND hWnd = GetSafeHwnd();
AfxBeginThread(ThreadProc; hWnd; THREAD_PRIORITY_NORMAL);
}
添加的代码将调用ThreadProc(),这个函数是新添加的线程的控制函
数,所以还需要在程序中添加这个函数。
在ThreadView。cpp中OnStartthread()的上面添加函数ThreadProc
()。
l 注意:
…………………………………………………………Page 641……………………………………………………………
l 这个函数是一个全局函数,而并非是CThreadView类的成员函数,
尽管它在CThreadView类的执行文件中。
在函数ThreadProc()中添加如下代码:
UINT ThreadProc(LPVOID param)
{
::MessageBox((HWND)param; 〃Thread activated。〃; 〃Thread〃; MB_OK);
return 0;
}
这个线程实际上并没有作什么,它仅仅报告它被启动了。
在函数前面的两个冒号表明是在调用全局函数,对于Windows程序员
来说,这通常称为API或SDK调用。
当你运行这个程序后,主窗口出现。选择 “线程”菜单中的 “启动线
程”菜单选项,系统启动一个线程,并且显示一个消息框,如图12。1
所示。
图12。 1 线程启动消息框
第二节 线程间通信
通常,一个次要的线程为主线程执行一定的任务,这也暗示这在主线
程和次要线程之间需要有一个联系的渠道。有几种方法可以完成这些
联系任务:使用全局变量、使用CEven类或者使用消息。本节将介绍
这几种方法。
(1) 使用全局变量通信
假定你需要你的程序能够停止线程。你需要一个告诉线程何时停止的
方法。一种方法是建立一个全局变量,然后让线程监督这个全局变量
是否为标志线程终止的值。为了实现这种方法,按照如下步骤修改前
面创建的Thread程序。
1。 在 “线程”菜单中添加菜单项 “停止线程”,ID为
…………………………………………………………Page 642……………………………………………………………
ID_STOPTHREAD。
2。 为 “停止线程”添加消息处理函数OnStopthread()。
3。 在ThreadView。cpp文件中添加一个全局变量threadController。
添加方法是在ThreadView。cpp的最上面,在endif下面添加下面的语
句:
volatile int threadController;
关键字volatile表示你希望这个变量可以被外面使用它的线程修改。
4。 修改OnStartthread()函数,代码如下所示:
void CThreadView::OnStartthread()
{
// TODO: Add your mand handler code here
threadController = 1;
HWND hWnd = GetSafeHwnd();
AfxBeginThread(ThreadProc; hWnd; THREAD_PRIORITY_NORMAL);
}
现在你可能已经猜到threadController的值决定线程是否继续。
5。 在OnStopthread()函数中添加下列代码:
threadController = 0;
6。 修改ThreadProc()函数的代码,代码如下:
UINT ThreadProc(LPVOID param)
{
::MessageBox((HWND)param; 〃Thread activated。〃; 〃Thread〃; MB_OK);
while (threadController == 1)
{
;
}
…………………………………………………………Page 643……………………………………………………………
::MessageBox((HWND)param; 〃Thread stopped。〃; 〃Thread〃; MB_OK);
return 0;
}
现在线程首先显示一个消息框,告诉用户线程被启动了。然后通过一
个while循环检查全局变量threadController,等待它的值变成0。尽
管这个while循环微不足道,但是你在这里可以加上执行你希望的任
务的代码。
现在编译并运行这个程序,选择 “线程”菜单中的 “启动线程”菜单
项启动一个线程,这是弹出如图12。1所示的对话框。然后选择 “线
程”主菜单中的 “停止菜单”菜单项,这时弹出如图12。2所示的对话
框,告诉用户线程已经终止。
图12。 2 线程关闭消息框
(2) 使用用户自定义消息通信
现在你有了一个简单的用于从主线程中联系附加线程的方法。反过
来,如何从附加线程联系主线程呢?最简单的实现这种联系的方法是
在程序中加入用户定义的Windows消息。
首先,要定义用户消息。这一步很容易,例如:
const WM_USERMSG = WM_USER + 100;
WM_USER变量是由Windows定义的,它是第一个有效的用户消息数。因
为你的程序的其它部分也会使用用户消息,故将新的用户消息
WM_USERMSG设置为WM_USER+100 。
在定义了用户消息之后,你应当在线程中调用::PostMessage()函数
来向主线程发送你所需要的消息。一般按照下面的方式调
用::PostMessage()函数:
::PostMessage((HWND)param; WM_USERMSG; 0; 0);
PostMessage()的四个参数分别是接收消息的窗口的句柄、消息的
ID、消息的WPARAM和LPARAM参数。
…………………………………………………………Page 644……………………………………………………………
将下面的语句加入到ThreadView。h中CThreadView类声明的上面。
const WM_THREADENDED = WM_USER + 100;
仍然是在此头文件中,在消息映射中加入下列语句,注意要加到
AFX_MSG的后面和DECLARE_MESSAGE_MAP的前面。
afx_msg LONG OnThreadended();
然后切回到ThreadView。cpp,在类的消息映射中加入下列语句,位置
在}}AFX_MSG_MAP之后。
ON_MESSAGE(WM_THREADENDED; OnThreadended)
再用下面的语句更改ThreadProc()函数。
UINT ThreadProc(LPVOID param)
{
::MessageBox((HWND)param; 〃Thread activated。〃; 〃Thread〃; MB_OK);
while (threadController == 1)
{
;
}
::PostMessage((HWND)param; WM_THREADENDED; 0; 0);
return 0;
}
在CThreadView中添加下面的成员函数。
LONG CThreadView::OnThreadended(WPARAM wParam; LPARAM lParam)
{
AfxMessageBox(〃Thread ended。〃);
return 0;
}
…………………………………………………………Page 645……………………………………………………………
图12。 3 线程终止对话框
当你重新运行这个程序时,选择 “线程”主菜单中的 “启动线程”菜
单选项,弹出一个消息框告诉你线程已经启动。为了结束这个线程,
选择 “线程”主菜单中的 “停止菜单”菜单选项,这将弹出一个如图
12。3所示的消息框告诉你线程已经停止。
(3) 使用Event对象通信
一个比较复杂的在两个线程间通信的方法是使用Event对象,在MFC下
也就是CEvent类对象。一个Event对象可以有两种状态:通信状态和
非通信状态。线程监视着Event对象的状态,并由此在合适的时间执
行它们的操作。
创建一个CEvent类的对象很简单,如下:
CEvent threadStart;
实际上,CEvent的构造函数形式如下:
CEvent( BOOL bInitiallyOwn = FALSE; BOOL bManualReset = FALSE;
LPCTSTR lpszName = NULL; LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
4个参数含义如下:
bInitiallyOwn 布尔量。如果值是True,用于CMultilock和
CSingleLock对象的线程将被允许。如果值为False,所有希望访问资
源的线程必须等待。缺省值为False。
bManualReset 布尔量。如果值为True,则Event对象是手动对象。如
果值为False,则Event对象是自动对象。缺省值为True。
lpszName CEvent对象的名称。如果事件对象被多个进程使用时必须
提供一个名称。缺省值为NULL。
lpsaAttribute CEvent对象的安全属性,与在Win32中的
SECURITY_ATTRIBUTES 相同。
尽管CEvent的构造函数有4个参数,但是经常不加任何参数的创建缺
…………………………………………………………Page 646……………………………………………………………
省的对象。当CEvent对象被创建之后,它 自动的处在未通信状态。为
了使其处在通信状态,可以调用其成员函数SetEvent ,如下所
示:
threadStart。SetEvent();
在执行完上述语句之后,threadStart将处在其通信状态。你的线程
应当监视它,这样才能知道何时执行。线程是通过调用如下Windows
API函数WaitForSingleObject()来监视CEvent对象的,形式如下:
::WaitForSingleObject(threadStart。m_hObject; INFINITE);
预定义的常量INFINITE告诉WaitForSingleObject()直到指定的
CEvent对象处在通信状态时才返回。换句话说,如果你把
WaitForSingleObject()放在线程的开头,系统将挂起线程直到
CEvent对象处在通信状态。当主线程准备好后,你应当调用SetEvent
()函数。
一旦线程不再挂起,它就可以运行了。但是,如果此时你还想和线程
通信,线程必须监视下一个CEvent对象处在通信状态,故你需要再次
调用WaitForSingleObject()函数,此时需要将等待时间设置为0,如
下所示:
::WaitForSingleObject(threadend。m_hObject; 0);
在这种情况下,如果WaitForSingleObject()返回值为
WAIT_OBJECT_0;则CEvent对象处在通信状态。否则,CEvent对象处在
非通信状态。
下面的例子说明如何使用CEvent类在两个线程间通信。按照以下步骤
进行:
1。 在ThreadView。cpp 中#include 〃ThreadView。h〃语句后面加上
#include 〃afxmt。h〃。
2。 在ThreadView。cpp 中volatile int threadController语句后加上
下列语句: