Figure 2 A CMultiThread Class Usage
//////////////////////////////////////////////////////////////////
class CMultiThread: CWinThread
{
public:
CMultiThread();
virtual ~CMultiThread();
protected:
virtual void DoWork(){} // override to do work on
// 2nd thread
}
Figure 3 Synchronization Objects
MFC Synchronization Object Win32 Synchronization Mechanism Purpose Scope Comments
CCriticalSection CRITICAL_SECTION Resource Within a Usually the simplest, most
data structure along protection process efficient way to protect
with EnterCritical- via mutual resources from simultaneous
Section and related exclusion. access attempts. Lowest
calls. overhead mutual-exclusion
mechanism.
CMutex CreateMutex Resource Across Use when you need to
protection processes protect resources across
via mutual processes; for example, to
exclusion. grant exclusive access to
global resources in a DLL (or
local COM component) shared
by different apps.
CSemaphore CreateSemaphore Resource Across Keeps a count of the
protection via processes number of threads with a
"metered" lock on a resource and
access. Often refuses additional locks
used to limit once the specified limit
access to a small is reached.
set of system
resources for
performance or
other purposes.
CEvent CreateEvent Wake-up calls to Across Examples include error-
a thread waiting processes logging and processing input
for work to be from distributed sources.
completed or
new input to
process.
Figure 4 Synchronizing Paintballs
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);
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
}
}
Figure 6 Sharing Data
class CThreadApp : public CWinApp
{
public:
CThreadSafeWnd* getThreadSafeWindow() const {return &m_tsw;}
CThreadApp();
private:
CThreadSafeWnd m_tsw;
// Override and implementation details go here
// ...
};
void CBallThread::SingleStep()
{
// code to calculate the new position, m_rectPosition
//...
((CThreadApp*) AfxGetApp())->
getThreadSafeWindow()->PaintCircle(m_color, m_rectPosition);
}
Figure 8 CMultiThread Class
// Multithrd.h : header file
//
////////////////////////////////////////////////////////////////////
class CMultiThread : public CWinThread
{
DECLARE_DYNCREATE(CMultiThread)
public:
CMultiThread() {}
virtual ~CMultiThread();
//CreateThread masks CWinThread::CreateThread
BOOL CreateThread(DWORD dwCreateFlags = 0,
UINT nStackSize = 0,
LPSECURITY_ATTRIBUTES
lpSecurityAttrs = NULL,
UINT nMilliSecs = INFINITE);
// upper time limit to wait
BOOL InitInstance() {return TRUE;}
int Run();
protected:
CEvent* m_pWorkEvent; // do work event
CEvent* m_pExitEvent; // used to synchronize
// destruction
int m_nCycleTime; // do work cycle time
BOOL m_bEndThread; // end the thread ?
virtual void DoWork() {} // over-ride to do work
CEvent* GetEvent() const {return m_pWorkEvent;} // cycle control event
int GetCycleTime() const {return m_nCycleTime;}
void SetCycleTime(int nMilliSecs) {m_nCycleTime = nMilliSecs;}
};
// Multithrd.cpp : implementation file
//
#include "stdafx.h"
#include "Multithrd.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CMultiThread, CWinThread)
/////////////////////////////////////////////////////////////////////////////
// MultiThread
BOOL CMultiThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize,
LPSECURITY_ATTRIBUTES lpSecurityAttrs,
UINT nMilliSecs)
{
m_nCycleTime = nMilliSecs;
m_bEndThread = FALSE;
// Create a non-signaled, manual-reset event to synchronize destruction
m_pExitEvent = new CEvent(FALSE, TRUE);
ASSERT(m_pExitEvent);
// Create a non-signaled, auto-reset event to wait on for work cycle
m_pWorkEvent = new CEvent();
ASSERT(m_pWorkEvent);
// Start second thread
return CWinThread::CreateThread(dwCreateFlags, nStackSize,
lpSecurityAttrs);
}
CMultiThread::~CMultiThread()
{
// Start up the other thread so it can complete.
// When it does, it will set the exit event and the object can be
// destructed.
m_bEndThread = TRUE;
m_pWorkEvent->SetEvent();
CSingleLock csl(m_pExitEvent);
csl.Lock(); // wait for 2nd thread to finish
csl.Unlock();
delete m_pWorkEvent;
delete m_pExitEvent;
}
int CMultiThread::Run()
{
CSingleLock csl(m_pWorkEvent); // synch on the work event
while (!m_bEndThread) // loop until we're done
{
csl.Lock(m_nCycleTime); // wait for event or timeout
csl.Unlock();
DoWork(); // and then do some work
}
m_pExitEvent->SetEvent(); // not waiting signal
return CWinThread::Run();
}
Figure 9 A Derived CMultiThread Class
// Asynch.h : header file
//
/////////////////////////////////////////////////////////////////////////////
class CGarbageObject : public CObject
{
public:
void* m_pGarbage;
};
/////////////////////////////////////////////////////////////////////////////
const int CleanTime = 5; // garbage cleanup limit
class CAsynchGarbageCollector : public CMultiThread
public:
CAsynchGarbageCollector();
virtual ~CAsynchGarbageCollector() {};
void CollectGarbage(void* pBuf); // get the garbage
protected:
CObList m_BuffList; // garbage list
CCriticalSection m_CritSect; // arbitrator for garbage list
virtual void DoWork(); // release garbage here
};
// Asynch.cpp : implementation file
//
#include "stdafx.h"
#include "multithrd.h"
#include "asynch.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
CAsynchGarbageCollector::CAsynchGarbageCollector()
{
CreateThread();
}
void CAsynchGarbageCollector::CollectGarbage(void* pBuf)
{
CGarbageObject* pObj = new CGarbageObject;
ASSERT(pObj);
pObj->m_pGarbage = pBuf; // create fresh garbage object
CSingleLock csl(&m_CritSect); // be safe
csl.Lock(); // when accessing garbage list
m_BuffList.AddTail(pObj); // add the garbage to the list
int count = m_BuffList.GetCount();
csl.Unlock();
// if it's time, release the garbage on the other thread
if (count >= CleanTime)
GetEvent()->SetEvent();
}
void CAsynchGarbageCollector::DoWork()
{
if (!m_BuffList.IsEmpty())
{
CSingleLock csl(&m_CritSect); // be safe
csl.Lock(); // when accessing garbage list
while (!m_BuffList.IsEmpty())
{
// We could check the buffers before release
// Now remove the garbage
delete
(static_cast<CGarbageObject*>
(m_BuffList.GetHead()))->m_pGarbage;
delete m_BuffList.RemoveHead();// and the list entry
}
csl.Unlock();
}
// Now we could defragment the released buffers
}