友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
第三电子书 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

C语言实例教程(PDF格式)-第73部分

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!




在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语句后加上 

下列语句:  



CEvent threadStart;  



CEvent threadEnd;  



删除语句volatile int threadController。  


…………………………………………………………Page 647……………………………………………………………

3。 用下面的代码更换ThreadProc()函数。  



UINT ThreadProc(LPVOID param)  



{  



::WaitForSingleObject(threadStart。m_hObject; INFINITE);  



::MessageBox((HWND)param; 〃Thread activated。〃; 〃Thread〃; MB_OK);  



BOOL keepRunning = TRUE;  



while (keepRunning)  



{  



int result = ::WaitForSingleObject(threadEnd。m_hObject; 0);  



if (result == WAIT_OBJECT_0)  



keepRunning = FALSE;  



}  



::PostMessage((HWND)param; WM_THREADENDED; 0; 0);  



return 0;  



}  



4。 用下面的语句替换OnStartthread()函数中的内容。  



threadStart。SetEvent();  



5。 用下面的语句替换OnStopthread()函数中的内容。  



threadEnd。SetEvent();  



6。             使用ClassWizard为CthreadView处理WM_CREATE消息的函数 

OnCreate(),并在TODO后面添加代码。OnCreate()函数如下所示:  



int CThreadView::OnCreate(LPCREATESTRUCT lpCreateStruct)   



{  



if (CView::OnCreate(lpCreateStruct) == …1)  



return …1;  



// TODO: Add your specialized creation code here  


…………………………………………………………Page 648……………………………………………………………

HWND hWnd = GetSafeHwnd();  



AfxBeginThread(ThreadProc; hWnd);  



return 0;  



}  



编译并运行这个程序,新版本的程序运行起来和旧版本的程序一样, 

但是,新版本的程序为了实现在主线程和次要线程间通信,既使用了 

CEvent类,又使用了用户定义的Windows消息。  



新版本的程序和旧版本的程序的一个大的不同在于次要线程在 

OnCreate()函数中被启动。然而由于线程函数的第一行即调用 

WaitForSingleObject(),所以此线程立即被挂起并且等待 

threadStart处于通信状态。  



当threadStart处在通信状态时,新线程显示消息框,然后进入while 

循环。这个while循环继续执行直到threadEnd处在通信状态,然后线 

程向主线程发送一个WM_THREADENDED消息并退出。因为此线程是在 

OnCreate()函数中被创建的;一旦结束,不会被重新启动。  



    



                   第三节 线程同步  



使用多线程可以带来一些非常有趣的问题。例如,如何防止两个线程 

在同一时间访问同一数据?例如,假设一个线程正在更新一个数据 

集,而同时另外一个线程正在读取数据集,结果如何?第二个线程将 

会读取到错误的数据,因为数据集中只有一部分元素被更新过。  



保持在同一个进程内的线程工作协调一致称之为线程同步。Event对 

象实际上就是线程同步的一种形式。在本节中,你将会学到三种使你 

的多线程程序更安全的线程同步对象—critical  section、互斥对象 

 (mutex)、信号量 (semaphore)。  



   (1) 使用Critical Section  



Critical  Section是一种保证在一个时间只有一个线程访问数据集的 

非常简单的方法。当你使用Critical  Section,你给了线程一个它们 

必须共享的对象。任何拥有Critical  Section对象的线程可以访问被 

保护起来的数据。其它线程必须等待直到第一个线程释放了Critical  

Section对象,此后其它线程可以按照顺序抢 占Critical   Section对 


…………………………………………………………Page 649……………………………………………………………

象,访问数
返回目录 上一页 下一页 回到顶部 1 1
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!