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
}