深入浅出Win32多线程设计之MFC的多线程(I)

1、创建和终止线程   在MFC 程序中创建一个线程,宜调用AfxBeginThread函数。该函数因参数不同而具有两种重载版本,分别对应工作者线程和用户接口(UI)线程。   工作者线程
CWinThread *AfxBeginThread(  AFX_THREADPROC pfnThreadProc, //控制函数  LPVOID pParam, //传递给控制函数的参数  int nPriority = THREAD_PRIORITY_NORMAL, //线程的优先级  UINT nStackSize = 0, //线程的堆栈大小  DWORD dwCreateFlags = 0, //线程的创建标志  LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL //线程的安全属性 );
  工作者线程编程较为简单,只需编写线程控制函数和启动线程即可。下面的代码给出了定义一个控制函数和启动它的过程:
//线程控制函数 UINT MfcThreadProc(LPVOID lpParam) {  CExampleClass *lpObject = (CExampleClass*)lpParam;  if (lpObject == NULL || !lpObject->IsKindof(RUNTIME_CLASS(CExampleClass)))   return - 1; //输入参数非法  //线程成功启动  while (1)  {   ...//  }  return 0; } //在MFC程序中启动线程 AfxBeginThread(MfcThreadProc, lpObject);
  UI线程   创建用户界面线程时,必须首先从CWinThread 派生类,并使用 DECLARE_DYNCREATE 和 IMPLEMENT_DYNCREATE 宏声明此类。   下面给出了CWinThread类的原型(添加了关于其重要函数功能和是否需要被继承类重载的注释):
class CWinThread : public CCmdTarget {  DECLARE_DYNAMIC(CWinThread)  public:   // Constructors   CWinThread();   BOOL CreateThread(DWORD dwCreateFlags = 0, UINT nStackSize = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);   // Attributes   CWnd* m_pMainWnd; // main window (usually same AfxGetApp()->m_pMainWnd)   CWnd* m_pActiveWnd; // active main window (may not be m_pMainWnd)   BOOL m_bAutoDelete; // enables 'delete this' after thread termination   // only valid while running   HANDLE m_hThread; // this thread's HANDLE   operator HANDLE() const;   DWORD m_nThreadID; // this thread's ID   int GetThreadPriority();   BOOL SetThreadPriority(int nPriority);   // Operations   DWORD SuspendThread();   DWORD ResumeThread();   BOOL PostThreadMessage(UINT message, WPARAM wParam, LPARAM lParam);   // Overridables   //执行线程实例初始化,必须重写   virtual BOOL InitInstance();   // running and idle processing   //控制线程的函数,包含消息泵,一般不重写   virtual int Run();   //消息调度到TranslateMessage和DispatchMessage之前对其进行筛选,   //通常不重写   virtual BOOL PreTranslateMessage(MSG* pMsg);   virtual BOOL PumpMessage(); // low level message pump   //执行线程特定的闲置时间处理,通常不重写   virtual BOOL OnIdle(LONG lCount); // return TRUE if more idle processing   virtual BOOL IsIdleMessage(MSG* pMsg); // checks for special messages   //线程终止时执行清除,通常需要重写   virtual int ExitInstance(); // default will 'delete this'   //截获由线程的消息和命令处理程序引发的未处理异常,通常不重写   virtual LRESULT ProcessWndProcException(CException* e, const MSG* pMsg);   // Advanced: handling messages sent to message filter hook   virtual BOOL ProcessMessageFilter(int code, LPMSG lpMsg);   // Advanced: virtual access to m_pMainWnd   virtual CWnd* GetMainWnd();   // Implementation  public:   virtual ~CWinThread();   #ifdef _DEBUG    virtual void AssertValid() const;    virtual void Dump(CDumpContext& dc) const;    int m_nDisablePumpCount; // Diagnostic trap to detect illegal re-entrancy   #endif   void CommonConstruct();   virtual void Delete();   // 'delete this' only if m_bAutoDelete == TRUE   // message pump for Run   MSG m_msgCur; // current message  public:   // constructor used by implementation of AfxBeginThread   CWinThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam);   // valid after construction   LPVOID m_pThreadParams; // generic parameters passed to starting function   AFX_THREADPROC m_pfnThreadProc;   // set after OLE is initialized   void (AFXAPI* m_lpfnOleTermOrFreeLib)(BOOL, BOOL);   COleMessageFilter* m_pMessageFilter;  protected:   CPoint m_ptCursorLast; // last mouse position   UINT m_nMsgLast; // last mouse message   BOOL DispatchThreadMessageEx(MSG* msg); // helper   void DispatchThreadMessage(MSG* msg); // obsolete };
  启动UI线程的AfxBeginThread函数的原型为:
CWinThread *AfxBeginThread(  //从CWinThread派生的类的 RUNTIME_CLASS  CRuntimeClass *pThreadClass,  int nPriority = THREAD_PRIORITY_NORMAL,  UINT nStackSize = 0,  DWORD dwCreateFlags = 0,  LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
  我们可以方便地使用VC++ 6.0类向导定义一个继承自CWinThread的用户线程类。下面给出产生我们自定义的CWinThread子类CMyUIThread的方法。   打开VC++ 6.0类向导,在如下窗口中选择Base Class类为CWinThread,输入子类名为CMyUIThread,点击"OK"按钮后就产生了类CMyUIThread。
  其源代码框架为:
/ // CMyUIThread thread class CMyUIThread : public CWinThread {  DECLARE_DYNCREATE(CMyUIThread)  protected:   CMyUIThread(); // protected constructor used by dynamic creation   // Attributes  public:   // Operations  public:   // Overrides   // ClassWizard generated virtual function overrides   //{{AFX_VIRTUAL(CMyUIThread)   public:    virtual BOOL InitInstance();    virtual int ExitInstance();   //}}AFX_VIRTUAL   // Implementation  protected:   virtual ~CMyUIThread();   // Generated message map functions   //{{AFX_MSG(CMyUIThread)    // NOTE - the ClassWizard will add and remove member functions here.   //}}AFX_MSG  DECLARE_MESSAGE_MAP() }; / // CMyUIThread IMPLEMENT_DYNCREATE(CMyUIThread, CWinThread) CMyUIThread::CMyUIThread() {} CMyUIThread::~CMyUIThread() {} BOOL CMyUIThread::InitInstance() {  // TODO: perform and per-thread initialization here  return TRUE; } int CMyUIThread::ExitInstance() {  // TODO: perform any per-thread cleanup here  return CWinThread::ExitInstance(); } BEGIN_MESSAGE_MAP(CMyUIThread, CWinThread) //{{AFX_MSG_MAP(CMyUIThread) // NOTE - the ClassWizard will add and remove mapping macros here. //}}AFX_MSG_MAP END_MESSAGE_MAP()
  使用下列代码就可以启动这个UI线程:
CMyUIThread *pThread; pThread = (CMyUIThread*) AfxBeginThread( RUNTIME_CLASS(CMyUIThread) );
  另外,我们也可以不用AfxBeginThread 创建线程,而是分如下两步完成:   (1)调用线程类的构造函数创建一个线程对象;   (2)调用CWinThread::CreateThread函数来启动该线程。   在线程自身内调用AfxEndThread函数可以终止该线程:
void AfxEndThread(  UINT nExitCode //the exit code of the thread );
  对于UI线程而言,如果消息队列中放入了WM_QUIT消息,将结束线程。   关于UI线程和工作者线程的分配,最好的做法是:将所有与UI相关的操作放入主线程,其它的纯粹的运算工作交给独立的数个工作者线程。   候捷先生早些时间喜欢为MDI程序的每个窗口创建一个线程,他后来澄清了这个错误。因为如果为MDI程序的每个窗口都单独创建一个线程,在窗口进行切换的时候,将进行线程的上下文切换!

  2.线程间通信   MFC中定义了继承自CSyncObject类的CCriticalSection 、CCEvent、CMutex、CSemaphore类封装和简化了WIN32 API所提供的临界区、事件、互斥和信号量。使用这些同步机制,必须包含"Afxmt.h"头文件。下图给出了类的继承关系:

  作为CSyncObject类的继承类,我们仅仅使用基类CSyncObject的接口函数就可以方便、统一的操作CCriticalSection 、CCEvent、CMutex、CSemaphore类,下面是CSyncObject类的原型:
class CSyncObject : public CObject {  DECLARE_DYNAMIC(CSyncObject)  // Constructor  public:   CSyncObject(LPCTSTR pstrName);   // Attributes  public:   operator HANDLE() const;   HANDLE m_hObject;   // Operations   virtual BOOL Lock(DWORD dwTimeout = INFINITE);   virtual BOOL Unlock() = 0;   virtual BOOL Unlock(LONG /* lCount */, LPLONG /* lpPrevCount=NULL */)   { return TRUE; }   // Implementation  public:   virtual ~CSyncObject();   #ifdef _DEBUG    CString m_strName;    virtual void AssertValid() const;    virtual void Dump(CDumpContext& dc) const;   #endif   friend class CSingleLock;   friend class CMultiLock; };
  CSyncObject类最主要的两个函数是Lock和Unlock,若我们直接使用CSyncObject类及其派生类,我们需要非常小心地在Lock之后调用Unlock。   MFC提供的另两个类CSingleLock(等待一个对象)和CMultiLock(等待多个对象)为我们编写应用程序提供了更灵活的机制,下面以实际来阐述CSingleLock的用法:
class CThreadSafeWnd {  public:   CThreadSafeWnd(){}   ~CThreadSafeWnd(){}   void SetWindow(CWnd *pwnd)   {    m_pCWnd = pwnd;   }   void PaintBall(COLORREF color, CRect &rc);  private:   CWnd *m_pCWnd;   CCriticalSection m_CSect; }; void CThreadSafeWnd::PaintBall(COLORREF color, CRect &rc) {  CSingleLock csl(&m_CSect);  //缺省的Timeout是INFINITE,只有m_Csect被激活,csl.Lock()才能返回  //true,这里一直等待  if (csl.Lock()) ;  {   // not necessary   //AFX_MANAGE_STATE(AfxGetStaticModuleState( ));   CDC *pdc = m_pCWnd->GetDC();   CBrush brush(color);   CBrush *oldbrush = pdc->SelectObject(&brush);   pdc->Ellipse(rc);   pdc->SelectObject(oldbrush);   GdiFlush(); // don't wait to update the display  } }
  上述实例讲述了用CSingleLock对Windows GDI相关对象进行保护的方法,下面再给出一个其他方面的例子:
int array1[10], array2[10]; CMutexSection section; //创建一个CMutex类的对象 //赋值线程控制函数 UINT EvaluateThread(LPVOID param) {  CSingleLock singlelock;  singlelock(&section);  //互斥区域  singlelock.Lock();  for (int i = 0; i < 10; i++)   array1[i] = i;  singlelock.Unlock(); } //拷贝线程控制函数 UINT CopyThread(LPVOID param) {  CSingleLock singlelock;  singlelock(&section);  //互斥区域  singlelock.Lock();  for (int i = 0; i < 10; i++)   array2[i] = array1[i];  singlelock.Unlock(); } } AfxBeginThread(EvaluateThread, NULL); //启动赋值线程 AfxBeginThread(CopyThread, NULL); //启动拷贝线程
  上面的例子中启动了两个线程EvaluateThread和CopyThread,线程EvaluateThread把10个数赋值给数组array1[],线程CopyThread将数组array1[]拷贝给数组array2[]。由于数组的拷贝和赋值都是整体行为,如果不以互斥形式执行代码段:
for (int i = 0; i < 10; i++) array1[i] = i;
  和
for (int i = 0; i < 10; i++) array2[i] = array1[i];
  其结果是很难预料的!   除了可使用CCriticalSection、CEvent、CMutex、CSemaphore作为线程间同步通信的方式以外,我们还可以利用PostThreadMessage函数在线程间发送消息:
BOOL PostThreadMessage(DWORD idThread, // thread identifier UINT Msg, // message to post WPARAM wParam, // first message parameter LPARAM lParam // second message parameter );
  3.线程与消息队列   在WIN32中,每一个线程都对应着一个消息队列。由于一个线程可以产生数个窗口,所以并不是每个窗口都对应着一个消息队列。下列几句话应该作为"定理"被记住:   "定理" 一   所有产生给某个窗口的消息,都先由创建这个窗口的线程处理;   "定理" 二   Windows屏幕上的每一个控件都是一个窗口,有对应的窗口函数。   消息的发送通常有两种方式,一是SendMessage,一是PostMessage,其原型分别为:
LRESULT SendMessage(HWND hWnd, // handle of destination window  UINT Msg, // message to send  WPARAM wParam, // first message parameter  LPARAM lParam // second message parameter ); BOOL PostMessage(HWND hWnd, // handle of destination window  UINT Msg, // message to post  WPARAM wParam, // first message parameter  LPARAM lParam // second message parameter );
  两个函数原型中的四个参数的意义相同,但是SendMessage和PostMessage的行为有差异。SendMessage必须等待消息被处理后才返回,而PostMessage仅仅将消息放入消息队列。SendMessage的目标窗口如果属于另一个线程,则会发生线程上下文切换,等待另一线程处理完成消息。为了防止另一线程当掉,导致SendMessage永远不能返回,我们可以调用SendMessageTimeout函数:
LRESULT SendMessageTimeout(  HWND hWnd, // handle of destination window  UINT Msg, // message to send  WPARAM wParam, // first message parameter  LPARAM lParam, // second message parameter  UINT fuFlags, // how to send the message  UINT uTimeout, // time-out duration  LPDWORD lpdwResult // return value for synchronous call );
  4. MFC线程、消息队列与MFC程序的"生死因果"   分析MFC程序的主线程启动及消息队列处理的过程将有助于我们进一步理解UI线程与消息队列的关系,为此我们需要简单地叙述一下MFC程序的"生死因果"(侯捷:《深入浅出MFC》)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值