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
}