
Critical section
MFC encapsulates the critical section within a
CCriticalSection class.
Critical section is the easiest of the thread control
devices. It is only useful from within a single process. It essentially stops
all other threads from enter a critical section until the first thread has
released it. If multiple threads are trying to access a locked critical section,
there is no guarantee which thread will get it when it becomes free.
Generally, I declare a CCriticalSection somewhere in my
code, generally as a class member but sometimes as a standalone global.
Thereafter, it protects access to resources with a Lock() and releases the
CCriticalSection for other threads with an Unlock.
#include <afxmt.h>
class CSomething
{
.
public:
.
. bool bOne();
protected:
CCriticalSection m_cs;
.
.
};
bool CSomething::bOne()
{
BOOL bLocked = m_cs.Lock( 100 );
.
.
if (bLocked)
m_cs.Unlock();
return bLocked;
}
Mutex
Semaphore
Event
Terminating one thread from another
Lets assume that we have a worker thread running in a loop
and that it has done something really bad and we’ve flown in a guy from a Texas
death row to terminate it. Just to make things easy for this guy because he is,
after all, expensive and you know what they say, time is money, we’ve set the
worker thread up to be cooperative. All we have to do is signal it by setting a
flag and it will cease looping and self terminate.
If we don’t care about waiting until the thread has
terminated then we don’t have anything else to talk about. On the other hand,
sometimes you need to verify the end of the thread. There are two basic ways of
this, the worker thread does something special to notify the world like set a
flag or an event or the outside agent monitors the state of the thread’s handle.
When the handle stops being valid, the thread has ceased. The big advantage of
this is that this happens when the thread is completely gone. The disadvantage
is the caller has to have some way of gaining access to the thread’s handle. As
for the flag method, the thread can set it as it exits but that leaves you in a
race condition as well as having to forge some mechanism that makes a flag
available to both a caller and the worker thread. Events are less closely bonded
but the thread is not yet closed out at the time that it is telling the world
that it is.
There’s one other little thing to worry about while waiting
for the worker to finish up. If the outside agent is hung in a loop awaiting the
thread's demise, then it isn’t doing much that is useful. If said outside agent
is operating on the message pump’s dime then no other messages are going to be
handled unless we do something special. On the other hand, if we don’t care
about message pump then that makes life easier. In any case, it is entirely
possible that the thread termination may have something untoward going on such
that the thread termination notification never gets a chance to happen. We do
not want our terminator to get hung up so he should have a good escape clause
written into his contract.
Let’s assume that the thread is executing within the
confines of a class and that this thread is monitoring the state of m_bTerminate
to flag when it is to quit. The class takes care of both the flag and the thread
handle. Further, let’s assume that we can get here as the result of a message so
we want to provide for the processing of other messages. If we don’t care about
unsigned long WINAPI
ThreadProc(void *pThreadProc)
{
CThreadProc* pTP = (CThreadProc *)pThreadProc;
if (pTP)
{
pTP->m_bTerminate = false;
return pTP->uiThreadProc()
}
else
return 0;
}
UINT CThreadProc::uiThreadProc()
{
while (!m_bTerminate)
Sleep( 50 );
return 0;
}
void CThreadProc::Terminate()
{
// Tell the thread that it is time to quit.
m_bTerminate = true;
// Calculate a time out of 2 seconds from now.
DWORD dwTimeOut = GetTickCount() + 2000;
// Wait for the thread handle to go invalid or the timeout to happen.
while ( !(AtlWaitWithMessageLoop( m_hThread )) && (dwTimeOut<GetTickCount())
)
{
// Yield a bit of the time slice while we wait for
// the thread to terminate.
Sleep(50);
}
}

|