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(§ion); //互斥区域 singlelock.Lock(); for (int i = 0; i < 10; i++) array1[i] = i; singlelock.Unlock(); } //拷贝线程控制函数 UINT CopyThread(LPVOID param) { CSingleLock singlelock; singlelock(§ion); //互斥区域 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 ); |