网站首页/技术开发列表/内容

用MFC编写多线程程序案例

技术开发2024-04-08阅读
作者:林俊  

      线程技术使不同的代码可以同时运行。当然,只有在多C P U的计算机上,多个线程才能够真正地同时运行。然而,由于操作系统把C P U的时间分成很短的片段分配给每个线程,这样给人的感觉好像是多个线程真的同时运行。
     线程的概念与1 6位环境中的多任务有很大的不同。或许曾听人们这样讲: Win32是一种抢占式操作系统,而Windows 3.1 是一种协作式的多任务环境。其关键区别在于:在抢占式多任务环境中,操作系统负责管理哪个线程在什么时候执行。如果当线程1暂停执行时,线程2才有机会获得CPU时间,我们说线程1是抢占的。如果某个线程的代码陷入死循环,这并不可怕,操作系统仍会安排时间给其他线程。在Windows 3.1下,程序员必须保证应用程序能够把控制权返还给Windows。如果这一步失败,将导致整个操作环境锁死,或许你已经有过这样的痛苦经历。只要稍微想想便会明白, 16位的Windows是如此脆弱,它依赖于应用程序的运行情况,并且不允许程序陷入死循环或无穷递归以及任何封闭状态。这是因为所有的应用程序都必须协助Windows工作,这种工作类型被称为协作式多任务系统。
     在很多情况下,需要采用多线程技术进行程序设计。例如,常用的字处理软件Word,当输入文字的时候,Word同时进行拼写和语法的检验,也就是将文档中的词语与词库中的词语进行比较,并对文档中的语句进行语法分析。这些操作都比较耗费时间,但是我们在使用Word的时候并没有感觉到输入过程有明显的滞后现象。这里Word就采用了多线程技术,其中一个线程接收输入,另一个线程进行拼写和语法的检验。
     而对于在VC下编写多线程的程序有多种方法可以直接使用WINDOWS提供的API函数编写,当然最为方便的还是使用MFC编写,今天我们在这里以几个具体的例子来说明一下如何用MFC来编写多线程程序。
     ~~一、用户界面线程示例:
     在这个例子中我们要学会如何创建一个可以单独执行的功能,且可以和应用程序同时运行的线程,而且该线程需要自己的用户界面,也就是说用户的操作和你程序的运算不会有干扰。例如在文档应用程序中的查询和替换功能。在这个例子中我们需要使用框架中的AfxBegin Thread()函数来创建用户界面线程。这将对线程具有完全控制权,我们将创建自己的CWinThread派生线程类。
     具体的步骤如下:
     ~1)创建新的线程类
     使用Class Wizard创建CWinThread派生线程类。例如创建无模式对话框的线程类,请参考程序清单—用户界面线程类。在本例中创建无模式对话框而不是有模式对话框的原因是,允许消息从主应用程序连续地转发到线程。
     ~2)创建用户界面线程
     为启动线程可以使用如下代码:
     C WinThread *pThread = AfxBeginThread(RUNTIME_CLASS(CWzdThread));
     线程需要调用: : PostQuitMessage(arg)来终止,这里的arg参数需要用户自己定义。应用程序为了获得arg的值,可以调用如下代码:
     int arg = pThread -> GetExitCodeThread();
     注意对于应用程序直接结束线程没有推荐的方式。线程必须自己退出并允许将自身清除。用户需要做的是创建Windows消息来通知线程终止。线程通过调用::PostQuitMessage (arg)来处理消息。
     ~3)注意:
     1、工作者线程倾向于琐碎的处理,与它不同的是,用户界面线程具有自己的界面而且实际上类似于运行其他应用程序。创建线程而不是其他应用程序的好处是线程可与应用程序共享程序空间,这样可以简化线程与应用程序共享数据的功能。
     2、典型情况是用户界面线程用于完成查询和替换等功能,或者是其他不希望占用主应用程序大量处理时间但是需要一个界面的功能或服务,或者用户也可完全不考虑界面,将这种类型的线程用于窗口消息服务器作为一种传递其消息的方式,以避免使自己因占用处理时间过多而陷入困境。
     3、在时间要求严格的应用程序(例如实时应用程序)中,不希望因为工作者线程启动而等待,这时可将工作者线程中的控制逻辑内置到用户界面线程中并提前创建线程。当需要处理事务时,向用户界面线程发送消息,此时用户界面线程已经运行并且在等待指令。
       程序清单:
     #if !defined(AFX_WZDTHREAD_H__411AE4C2_E515_11D1_9B80_00AA003D8695__INCLUDED_)
     #define AFX_WZDTHREAD_H__411AE4C2_E5151_1D1_9B80_00AA003D8695_ _INCLUDED _
     
     #if _MSC_VER >= 1000
     #pragma once
     #endif
     #include "WzdDialog.h"
     class CWzdThread : public CinThread
     {
     DECLARE_DYNCREATE( CWzdThread )
     protected:
     CWzdThread();
     public :
     virtual BOOL InitInstance();
     virtual int ExitInstance();
     protected:
     virtual ~CWzdThread();
     DECLARE_MESSAGE_MAP()
     private:
     CWzdDialog m_dlg;
     } ;
     #include "stdafx.h"
     #include "wzd.h"
     #include "WzdThread.h"
     #ifdef _DEBUG
     #define new DEBUG_NEW
     #undef THIS_FILE
     static char THIS_FILE[] = __FILE__;
     # end if
     IMPLEMENT_DYNCREATE( CWzdThread, CWinThread )
     CWzdThread::CWzdThread()
     {}
     BOOL CWzdThread::InitInstance()
     {
     m_dlg.Create( IDD_WZD_DIALOG );
     m_dlg.Show Window( SW_SHOW );
     m_pMainWnd = &m_dlg;
     return TRUE;
     }
     int CWzdThread::ExitInstance()
     {
     m_dlg.DestroyWindow( );
     return CWinThread : : ExitInstance( );
     }
     BEGIN_MESSAGE_MAP( CWzdThread, CWinThread )
     END_MESSAGE_MAP()
     ~~二、线程间的数据共享示例:
     在本例中演示了在几个线程之间进行程序数据共享和通讯,同时避免由于两个线程同时访问相同的数据而引发的冲突。在本例中使用了三种MFC类:CMutex、CSingleLock和CMultiLock来同步多个线程对一个数据类的同时访问。
     具体步骤:
     首先我们要先在线程中确定共享的数据类。在每个类定义中嵌入CMutex对象,如下所示:
     class CWzdData : public CObject
     {
     : : :
     CMutex m_mutex;
     : : :
     } ;
     如果数据类没有访问其数据的成员函数,这一步将添加它们。这些函数如下所示:
     void CWzdData::GetData( int *pInt,float *pFloat,DWORD *pWord )
     {
     *pInt = m_nInt;
     *pFloat = m_fFloat;
     *pWord = m_dwWord;
     }
     void CWzdData::SetData(int nInt,float fFloat,DWORD dwWord)
     {
     m_nInt = nInt;
     m_fFloat = fFloat;
     m_dwWord = dwWord;
     }
     在引用已嵌入CMutex变量的SetData()函数堆栈上创建CSingleLock类的实例。使用CSingleLock的Lock()函数避免在函数内部对数据多重访问,如下所示:
     BOOL CWzdData::SetData( int nInt,float fFloat,DWORD dwWord)
     {
     CSingleLock slock(&m_mutex);
     if (slock.Lock( 1000)) // 时间以毫秒记,
     {
     m_nInt = nInt;
     m_fFloat = fFloat;
     m_dwWord = dwWord ;
     return TRUE;
     }
     return FALSE;
     这段代码需要注意的是如果其他的线程同时访问这个数据, Lock()将立刻返回。否则, Lock()在指定的毫秒数内等待,直到超时并返回FALSE。如果在这个类中保存的数据与其他类中保存的数据相关,则在两个类中嵌入CMutex变量,两边都用CMultiLock等待,如下所示:
     CMutex mutex[2];
     mutex[0] = &mutex1;
     mutex[1] = &mutex2;
     CMultiLock mlock( mutex,2 ); // where 2 is the number of mutexes
     if (mlock.Lock(1000))
     { }
     CreateMutex()函数的功能并不仅仅只是追踪应用程序的实例。在该实例中只是简单使用其中的部分功能。
     具体的程序实现代码如下:
     #ifndef WZDDATA _ H
     #define WZDDATA _ H
     #include
     class CWzdData : public CObject
     {
     public:
     DECLARE_SERIAL( CWzdData )
     CWzdData();
     BOOL GetData(int *pInt,float *pFloat,DWORD *pWord);
     BOOL SetData(int nInt,float fFloat,DWORD dwWord);
     CMutex m_mutex;
     int m_nInt;
     float m_fFloat;
     DWORD m_dwWord ;
     } ;
     #endif
     
     #include "stdafx.h"
     #include "WzdData.h"
     
     IMPLEMENT_SERIAL( CWzdData, CObject, 0 )
     CWzdData::CWzdData()
     {
     m_nInt = 0;
     m_fFloat = 0.0f;
     m _ d w Word = 0;
     BOOL CWzdData::GetData( int *pInt,float *pFloat,DWORD *pWord )
     {
     CSingleLock slock( &m_mutex );
     if (slock.Lock(1000))
     {
     *pInt = m_nInt;
     *pFloat = m_fFloat;
     *pWord = m_dwWord;
     return TRUE;
     }
     return FALSE;
     这里以两个较为简单的多线程程序说明了一下如何使用MFC编写多线程程序,其实对于多线程程序的编写还有很多的技巧,这就需要大家自己多学多练了。
相关阅读