Download as pdf or txt
Download as pdf or txt
You are on page 1of 18

Articles » General Programming » Threads, Processes & IPC » Threads

Using Worker Threads


By Joseph M. Newcomer, 6 Apr 2001
4.93 (212 votes)

Introduction
Worker threads are an elegant solution to a number of problems about concurrent processing; for
example, the need to keep the GUI active while a computation is being performed. This essay addresses
some of the issues of using worker threads. A companion essay talks about some techniques dealing with
user-interface threads.

Why Worker Threads?

Consider a simple implementation of a program. For our purposes, the program has the task of inverting
the color of every pixel of an image, just because we sometimes need a concrete example to illustrate
techniques. For the sake of our example, we will assume that the image being processed is 10 megapixels
of 24-bit color.

The GUI has a menu item or other means of saying "Invert it now". This calls the doInvertmethod on the
view class:

void CMyView::doInvert()
{
for(int x=y = 0; y < image.height; y++)
for(int x = 0; x < image.width; x++)
changePixel(x, y);
}

This is a perfectly correct program. It traverses all 10 megapixels, happily changing the pixels, until it
completes. But it is not a good implementation.

Why not? Because the entire GUI is dead until the operation completes. This means that for some long
duration, the user is forced to wait while the operation proceeds, and there is absolutely nothing that can
be done about it. If the user decides that the transformation is bogus, and wants to stop it, well, tough. It
is going to complete anyway.

Doing it the obsolete, and hard, way


One solution, the antiquated and largely unused 16-bit Windows solution (but still used because it is "well
known"), is to use PeekMessage, an API call that does not block when there is no message.

void CMyView::doInvert()
{
running = TRUE;
for(int x=y = 0; running && y < image.height; y++)
for(int x = 0; running && x < image.width; x++)
{ /* change it */
MSG msg;
if(PeekMessage(&msg, AfxGetMainWnd()->m_hWnd,
0, 0, PM_REMOVE))
{ /* handle it*/
TranslateMessage(&msg);
DispatchMessage(&msg);
} /* handle it */
changePixel(x, y);
} /* change it */
}

This is bad for several reasons. The most important one is that it puts, in the time-critical main loop, a
function whose overhead is substantial. Now, instead of taking k minutes (whatever that was before) we
might find the algorithm takes a significant multiple of that to complete. But while it is running, the GUI is
still active. You could even, if you were not careful, fire off another thread to paint each green pixel purple.
This Is Not A Good Idea.

The performance hack is simple: only poll occasionally. For example, if we assume that the images are
roughly rectangular, we could change the code to

void CMyView::doInvert()
{
running = TRUE;
for(int y = 0; running && y < image.height; y++)
{ /* do one row */
MSG msg;
if(PeekMessage(&msg, AfxGetMainWnd()->m_hWnd,
0, 0, PM_REMOVE))
{ /* handle it*/
TranslateMessage(&msg);
DispatchMessage(&msg);
} /* handle it */

for(int x = 0; running && x < image.width; x++)


{ /* change it */
changePixel(x, y);
} /* change it */
} /* do one row */
}

Thus this tests only once per row, which can either be too often if the rows are short, or not often enough
if the rows are long. The generalization, changing the test to

if(x % 10 == 0 && PeekMessage(...))

will work if the rows are too short.

There are some problems remaining; for example, if there is a modeless dialog active, you will notice that
there is no IsDialogMessage call there to handle it. Oops. Nor, for that matter, is there code to handle
ActiveX event notifications. Oops. And it presumes that you are writing in pure C, and doing the dispatch
yourself. Life gets more complicated when you have to support the message maps and control paradigms
of MFC. Oops, squared.

But why bother when there is a better way?

The Thread Solution

It is almost always the case that you can use threads to do the job more easily. This is not without certain
costs and hazards, but it ultimately is the better method.

Here's a solution to handling the invert-everything. Note that we have to move from the pure-C domain
(which we interface to via a static method) to the MFC domain.

To the class (in this example, a CView-derived class), add the following declarations:

static UINT run(LPVOID p);


void run();
volatile BOOL running;

To start a thread, your handler does

void CMyView::doInvert()
{
running = TRUE;
AfxBeginThread(run, this);
}

UINT CMyView::run(LPVOID p)
{
CMyView * me = (CMyView *)p;
me->run();
return 0;
}

void CMyView::run()
{
for(int x=y = 0; running && y < image.height; y++)
for(int x = 0; running && x < image.width; x++)
changePixel(x, y);
running = FALSE;
}

The command to stop the thread is very simple:

void CMyView::OnStop()
{
running = FALSE;
}

That's all there is to it!

Well, almost. Keep reading.

For example, the above code assumes that the thread will not try to access any member variables of the
view unless it is certain they exist. This includes handles to synchronization primitives or pointers to a
CRITICAL_SECTIONthat might be used to synchronize interactions between the thread and its view. This
requires a more graceful shutdown mechanism.

Note that the declaration of the running variable includes the modifier v olatile. This is because under
certain optimizations, the compiler will discover that in the body of the loop there is absolutely nothing
that changes the running flag, and therefore, cleverly, it can avoid testing it each time through the loop.
This means that although you change the value in another thread, the change is never seen. By adding the
volatilemodifier, you tell the compiler that it cannot assume the variable will remain unmodified during
the execution of the loop, even though there is no code in the loop that can change the variable.

Worker threads and the GUI I: Enabling controls

The problem is that when your worker thread is running, there are probably lots of things you shouldn't be
doing. For example, starting a thread to do a computation. Then you'd have two threads running doing the
same or similar computations, and that way madness lies (we'll assume for the moment that this actually is
a Bad Thing).

Fortunately, this is easy. Consider your ON_UPDATE_COMMAND_UIhandler

void CMyView::OnUpdateInvertImage(CCmdUI * pCmdUI)


{
pCmdUI->Enable(!running && (whatever_used_to_be_here));
}

Generalizing this to cover other menu items is left as an Exercise For The Reader. However, note that this
explains why there is that assignment ru nning = FALSE;at the end of the thread handling routine
above: it explicitly forces the running flag to reflect the running status of the thread. (Well, if you are being
very pedantic, note that it is possible to start another thread before the current one finishes if the current
one does not quickly get back to test the running flag, so you may wish to use a separate Boolean variable
to indicate the thread state. Set it before the thread starts, and clear it only after the thread loop
completes. For most thread usages, a single running flag usually suffices.

Worker threads and the GUI II: Don't touch the GUI

That's right. A worker thread must not touch a GUI object. This means that you should not query the state
of a control, add something to a list box, set the state of a control, etc.

Why?

Because you can get into a serious deadlock situation. A classic example was posted on one of the
discussion boards, and it described something that had happened to me last year. The situation is this: you
start a thread, and then decide to wait for the thread to complete. Meanwhile, the thread does something
apparently innocuous, such as add something to a list box, or, in the example that was posted, calls
FindWindow. In both cases, the process came to a screeching halt, as all threads deadlocked.

Let's analyze these two situations in some detail, so you get a sense of what happened.

In my case, the list box sent a notification, via SendMessage, to its parent. This means the message went
to its parent thread. But the parent thread was blocked, waiting for the thread to complete. But the thread
couldn't complete until it could run, and guess what: the S endMessagewas a cross-thread SendMessage,
which would not return until it was processed. But the only thread that could process it was blocked, and
couldn't run until the thread completed. Welcome to the world of deadlock.

The Fi ndWindowproblem was quite similar. The programmer had specified finding a window with a
particular caption. This meant that the thread running F indWindowhad to SendMessagea WM_GETTEXT
message to the window whose handle it had just found via En umWindows. This message could not be
processed until the thread that owned the window could execute. But it couldn't, because it was blocked
waiting for the thread to finish. Deadlock. So note that although you should not touch the GUI thread
explicitly, you must also not touch it implicitly, through such innocuous-seeming operations such as
FindWindow, or you can, and will, experience deadlock. Under these conditions, by the way, there is no
recovery. You can kill the process, which explicitly terminates all threads, but this is neither elegant nor, as
the warning box tells us, necessarily safe.

How do you get around these problems?

In my case, I had just been sloppy. I actually know better, and have written as much. So much for being an
expert. The workaround in almost all cases is that you must never to a S endMessageto a GUI object. In
very rare cases, you can do a Po stMessage, although this usually won't work because you need to pass in
a pointer to a string, and cannot tell when the operation has finished so you can't tell when to release the
string. Any string you pass in must be allocated either in static storage (as a constant), or on the heap. If it
is allocated in writeable static storage, or on the heap, you need to know when the value can be reused or
freed. P ostMessagedoes not allow you to do this.

Therefore, the only solution is to use a user-defined message, and post it to the main GUI thread (usually
the main frame, but frequently a CV iew-derived object). The main GUI thread then handles the
SendMessageto the GUI object, and, knowing then when the operation has completed, is able to free up
any resources.

You must not send a pointer to a stack-allocated object in a P ostMessagecall. By the time the operation
begins to execute, the chances are excellent that the object will have been removed from the stack. The
results of this are Not A Pretty Sight.

This essay will not go into describing how to manage user-defined messages. That is covered in my essay
on Message Management. We will assume that there are some user-defined messages, with names like
UWM_ADDSTRING, that are already defined.

Here's some code that adds window names to a list box:

void CMyDialog::AddNameToControl(CString & name)


{
CString * s = new CString(name);
PostMessage(UWM_ADDSTRING, 0, (LPARAM)s;
}

To the collection of handlers in CMyDialog.h, I add the declaration

afx_msg LRESULT OnAddString(WPARAM, LPARAM);

To the MESSAGE_MAPof my class, I add

ON_REGISTERED_MESSAGE(UWM_ADDSTRING, OnAddString);

or

ON_MESSAGE(UWM_ADDSTRING, OnAddString)

(If you're curious about the distinction, read my essay on Message Management).

The handler then looks like this:

LRESULT CMyDialog::OnAddString(WPARAM, LPARAM lParam)


{
CString * s = (CString *)lParam;
c_ListBox.AddString(*s);
delete s;
return 0;
}

The sender of the message must allocate the value being sent on the heap, which it does via the ne w
operator. The message is posted to the main GUI thread, which eventually routes it to the O nAddString
handler. This particular handler knows that the message is destined for a particular control, c_ListBox (if you
don't understand how to get control variables for controls, read my essay on the subject). Note that we
could have put the destination control ID, or the destination CW nd-class variable, in wParam, and made
this more general. The handler calls the AddStringmethod. When this call completes, it is now known
that the string value is no longer required (this would be different if we had an owner-draw listbox without
LBS_HASSTRINGS, but if you already know how to do that, the solution should be evident). Therefore, we
can now delete the heap-allocated object, which for us is a C String.

Worker Threads and the GUI III: Dialogs and MessageBoxes

You must not try to launch any GUI window from a worker thread. This means a worker thread cannot call
on Me ssageBox, DoModal, Createof a modeless dialog, and so on. The only way you can handle this is to
post a user-defined message back to the main GUI loop to perform this service for you.

If you attempt to do this, you will get various odd failures. You will get A SSERTerrors from MFC, dialogs
will fail to come up, and essentially you will get all sorts of antisocial behavior from your application. If you
really, really need to have GUI objects operating from a thread, you must not use a worker thread. You
must use a user-interface thread, which has a message pump. I'm not an expert on these, although I've
done a brief article on what I discovered about them while doing a SAPI application, and this may be of
some assistance in using them.

If you must pause the thread until the dialog or M essageBoxcompletes, you should probably use an
Event for synchronization. What you would do is create, for example, an auto-reset Event, which is created
in the Cleared state. Post a user-defined message (see my article on Message Management) to the main
GUI thread, which will then launch the dialog. When the dialog completes, it calls ::SetEventto set the
event. Meanwhile, the thread, after posting the message, uses : :WaitForSingleObjectto wait for the
event to be set. The thread will block until the dialog completes and sets the event.

I really should check the VC++ 6.0 implementation of CE vent. The VC++ 4.2 implementation was so full
of errors as to be totally unusable. You Have Been Warned.

Worker Threads and the GUI IV: AfxGetMainWnd

Well, I've shown how to post a message to the window for the class. But what if you want to post a
message to the main window? This is obvious, right? Just do

AfxGetMainWnd()->PostMessage(...)

and you're done! Right? Of course not. It should be obvious by now that I wouldn't pose such a question if
the answer was the obvious one.

What will happen is either an AS SERTfailure or an access fault. If you exercise a bit of debugging
cleverness, you will find that the pointer to the main window is N ULL! But how can this be! The application
clearly has a main window...

Well, the answer is, yes, the application has a main window. But that's not what AfxGetMainWndis
defined as returning! Read the documentation carefully, and you will see that it says:

If AfxGetMainWndis called from the application's primary thread, it returns the application's main
window according to the above rules. If the function is called from a secondary thread in the
application, the function returns the main window associated with the thread that made the call.
[emphasis added]

A worker thread has no main window. Therefore, the call will return NULL. The workaround is to obtain a
pointer to the application's main window by calling AfxGetMainWndin the primary thread, and store it in a
place (such as a member variable of the class) where the worker thread can find it.

Waiting for a thread to start (27-Jan-01)

I recently processed a newsgroup message which was discussing the problem of waiting for thread startup.
The proposed mechanism looked like this:

BOOL waiting; // flag (this is one bug right here!)


void CMyClass::OnStartThread()
{
waiting = TRUE;
AfxBeginThread(myThread, something);
while(waiting) /* spin */ ; // horrible!
}

UINT CMyClass::myThread(LPVOID whatever) // *** static *** member function


{
... initialization sequence here
waiting = FALSE; // allow initiatior to continue (probably won't work)
... thread computations
return 0;
}

This code has several problems. First, it is a terrible implementation; it requires that the parent thread run
until its timeslice ends, which means that it delays the startup of the created thread. This alone is enough
reason to throw the code out. But even if it were acceptable, it is still wrong. The waiting variable must be
declared as v olatile, because under the optimizing compiler, the whileloop which spins may or may
not be executed, and if it is executed, it will probably never exit. I discuss this in more detail in my essay on
"Surviving the Release Build". But the bottom line is that when you have a variable of any sort that can be
modified from one thread and whose modification must be detected in another, you must declare it as
volatile.

The busy-wait is the serious disaster here. Since a timeslice, or quantum, is 200 ms in NT, it means that the
thread can waste up to 200 ms, doing nothing, before allowing the spawned thread to run. If the spawned
thread blocks on something like an I/O operation, and control returns to the creator, each time control
returns to the creator, it will burn up another 200ms (the kernel doesn't care that it is doing nothing but
polling a Boolean variable that can never change while it is polling it on a uniprocessor; it only knows that
the thread is executing). As you can see, it doesn't take very many I/O operations in the thread to add
many seconds to the perceived thread startup time.

The correct solution is shown below. In this solution, a manual-reset Event is used. To simplify the code, we
create it just before it is needed, and destroy it immediately afterwards; in the case where the thread may
be started up several times, the optimization to move this out to a member variable should be obvious.
Note that doing this as a member variable suggests that the Event would be created in the class's
constructor and destroyed in its destructor.

class CMyClass : public CView { // or something like this...


protected:
HANDLE startupEvent;
};

void CMyClass::OnStartThread()
{
startupEvent = ::CreateEvent(NULL, // no security attributes
TRUE, // manual-reset
FALSE,// initially non-signaled
NULL);// anonymous
AfxBeginThread(myThread, this);
switch(::WaitForSingleObject(startupEvent, MY_DELAY_TIME))
{ /* waitfor */
case WAIT_TIMEOUT:
... report problem with thread startup
break;
case WAIT_OBJECT_0:
... possibly do something to note thread is running
break;
} /* waitfor */
CloseHandle(startupEvent);
startupEvent = NULL; // make nice with handle var for debugging
}

UINT CMyClass::myThread(LPVOID me)


{
CMyClass * self = (CMyClass *)me;
self->run;
return 0;
}

void CMyClass::run( )
{
... long initialization sequence
::SetEvent(staruptEvent);
... loop computations
}

Note that I haven't done anything about dealing with the fact that the startup timeout may be incorrect
and the thread is still trying to start; this could be handled by, for example, attempting to
::WaitForSingleObjecton the thread handle with a wait time of 0; if it times out, the thread is
running; if it returns with WAIT_OBJECT_0the thread has halted. This requires that you deal with the
issues of the CW inThreadobject possibly being deleted before you can get the handle. No, I'm not going
to try to write every possible line of code.

Actually, it is rare that I would do something like this. I'd be more inclined to use messages posted to the
main window to establish state for the GUI: the thread is starting, the thread has started, the thread has
terminated (note that it may be terminated without being started). This avoids the issues about the GUI
blocking until the thread has actually completed the startup sequence, or dealing with the timeout issue if
the thread died somehow before doing the : :SetEvent.

void CMyClass::OnStartThread( )
{
AfxBeginThread(myThread, this);
PostMessage(UWM_THREAD_STARTING);
}

UINT CMyClass::myThread(LPVOID me) // *** static *** member function


{
CMyClass * self = (CMyClass *)me;
self->run( );
return 0;
}

void CMyClass::run( )
{
... lengthy startup sequence
PostMessage(UWM_THREAD_STARTED);
... long thread computation
PostMessage(UWM_THREAD_STOPPING);
... long thread shutdown
PostMessage(UWM_THREAD_TERMINATED);
}

I use the above paradigm in many contexts. Note that it completely eliminates the need for
synchronization, but adds some complexity to the GUI. For example, imagine that I have in the GUI (in this
case, since I'm posting to the view, it is view-specific state) a member variable that encodes the current
state: terminated-or-never-started, starting, stopping, and running. I might have menu items called Start
Computation, Pause Computation, Cancel Computation. I would create O N_UPDATE_COMMAND_UI
handlers that responed as follows:

void CMyClass::OnUpdateStart(CCmdUI * pCmdUI)


{
pCmdUI->Enable(threadstate == MY_THREAD_STOPPED);
}
void CMyClass::OnUpdatePause(CCmdUI * pCmdUI)
{
pCmdUI->Enable(threadstate == MY_THREAD_RUNNING);
}
void CMyClass::OnUpdateCancel(CCmdUI * pCmdUI)
{
pCmdUI->Enable(threadstate == MY_THREAD_RUNNING);
}

Providing I didn't really need to wait (and I find that I rarely do), I have now avoided the need to introduce
a blocking synchronization event in the main GUI thread, which could potentially lock up the GUI. Note
also that I might change the Cancel case to allow for the thread to be cancelled even if it is the middle of
starting, providing that this makes sense in the context of the thread computation. In this case, I'd have to
"poll" the cancel flag during the startup, for example, by splitting out the startup into a separate function:

void CMyClass::run( )
{
BOOL byCancel = FALSE;
if(!startMe( ))
{
PostMessage(UWM_THREAD_STOPPED, TRUE); // stopped by cancel
return;
}
PostMessage(UWM_THREAD_STARTED);
while(!cancelFlag)
{ /* thread loop */
...lengthy thread computation
} /* thread loop */

byCancel = cancelFlag;
PostMessage(UWM_THREAD_STOPPING);
...lengthy thread shutdown
PostMessage(UWM_THREAD_STOPPED, byCancel);
}

BOOL CMyClass::startMe( )
{
...do something
if(cancelFlag)
return FALSE;
...open the file on the server
if(cancelFlag)
{
...close the file on the server
return FALSE;
}
... more stuff, following above idea
return TRUE;
}

As usual in such cases, it is important to undo everything you have done if you detect the CancelFlag has
been set during startup. I've also defined WP ARAMof the message to include a flag that indicates if the
thread stopped because it stopped normally or stopped because the user cancelled it (which I might use to
display in the log that the thread was stopped by user request). I might also extend this to a set of
enumerated types to communicate back error codes in case the thread decided to terminate because of
some problem. I might even use L PARAMto hold the ::GetLastErrorcode. You see, there are many
themes-and-variations of this basic scheme.

Pausing a Thread and Thread Shutdown

A thread may have to stop and wait for some reason. Perhaps the user has clicked a "pause" check box or
pushed a "stop" button. Perhaps the thread has nothing to do, and is waiting for some information, such
as request packet, to process. The problem is that you need to shut down all the threads before a process
exits (note: in Windows CE, shutting down the main thread shuts down the process, and all threads
owned by the process. This is not true in Win9x, Windows NT, or Windows 2000). A typical bug
encountered is that you shut down your program, recompile it, and get an error that it is unable to write
the executable file. Why? Because the program is still running. But you don't see it on the taskbar. So you
bring up the Task Manager (via Ctrl-Alt-Del) and it isn't there, either. But something has got your
executable tied up! The answer is that if you look in the NT Task manager under the processes tab, or use
pview to look at processes, you will indeed find that your program is still running. This is usually because
you failed to shut down one or more worker threads. As long as any one thread exists, even if it is blocked,
your process is still alive. Of course, if you've killed the GUI thread, so the main window is gone and
nothing is visible. But it is still lurking. Of course, you can use the NT Task Manager, or pview, to kill the
process by terminating all of its threads, but this is a little bit like using dynamite to lift your car to change
the tire. Sure, it lifts the car, but there are a few other things that go wrong which are considered
undesirable side effects.

Three functions immediately present themselves for purposes of pausing or shutting down a thread: the
SuspendThreadand ResumeThreadmethods (and their underlying API calls, ::SuspendThreadand
::ResumeThread) and ::TerminateThread. Assume, for all practical purposes, except in some very
limited contexts, these functions do not exist. Using them will almost always get you in trouble.

The limited contexts in which these can be used are

A thread is generally free to call SuspendThread(or ::SuspendThread) on itself, providing it


knows itself to be in a "safe" state (for example, it is not holding any synchronization primitive it is
supposed to release). The risk here is that you don't know what your caller has locked, and you
could be suspending a thread that the caller of your function was not expecting to be suspended. So
be extremely careful even in this limited case!
A thread is always free to call ResumeThread(or ::ResumeThread) on another thread it knows is
suspended. For example, in the case of creating/starting a thread in suspended mode, you have to
call ResumeThread/::ResumeThreadto get it to run at all!

Note that it is not a good idea to have a thread call T erminateThreadon itself, because this will mean
that it terminates instantly. The implication of this is that the DLL_THREAD_DETACHevents for various
DLLs will not be executed, which can lead to the misbehavior of a DLL you didn't even know you were
using! (If you don't understand what this means, take it as meaning: bypassing D LL_THREAD_DETACHis a
Very Bad Thing). Instead, if a thread wants to kill itself, it should call ExitThread, which guarantees the
correct notification of all the DLLs.

Note that you should not substitute S uspendThread/ResumeThreadfor the proper use of
synchronization objects such as Events and Mutexes.

To illustrate why it is a Bad Idea to let one thread suspend another, let's take a simple case: you have a
worker thread off doing something, and the something involves memory allocation. You click the "Pause"
button on your GUI, which immediately calls S uspendThread. What happens? The worker thread stops.
Right now. Immediately. No matter what it is doing. If your worker thread happens to be in the storage
allocator, you have just shut down your entire application. Well, not quite--it won't shut down until the
next time you try to allocate memory from the GUI thread. But the MFC library is fairly liberal in using
memory allocation, so there is an excellent chance that the next call to the MFC library from any thread will
simply stop that thread dead in its tracks. If it is your GUI thread (the most likely one) your app appears to
hang.

Why is this?

The storage allocator is designed to be thread-safe. This means that at most one thread at a time is
permitted to be executing it. It is protected by a C RITICAL_SECTION, and each thread which attempts to
enter the allocator blocks if there is already a thread in the allocator. So what happens if you do
SuspendThread? The thread stops dead, in the middle of the allocator, with the critical section lock held.
This lock will not be released until the thread resumes. Now, if it happens that your GUI requires an
allocation as part of resuming the thread, an attempt to resume the thread will block, producing classic
deadlock. And if you did a :: TerminateThread, then there is no way the lock will ever be released. And
without Su spendThread, there is no need for ResumeThread.

Ah, you say, but I know I don't do any memory allocation either in the worker thread or the GUI. So I don't
need to worry about this!

You're wrong.

Remember that the MFC library does allocations you don't see. And allocation is only one of many critical
sections that exist inside the MFC library to make it thread-safe. And stopping in any of them will be fatal
to your app.

Ignore the existence of these functions.

So how do you suspend or terminate a thread?

The problems of shutting down a thread and pausing a thread are closely related, and my solution is the
same in both cases: I use a synchronization primitive to effect the pause by suspending the thread, and use
timeouts on the primitive to allow me to poll for shutdown.

Sometimes the synchronization primitive is a simple event, such as in the example below where I wish to be
able to pause the thread. In other cases, particularly where I'm using the thread to service a queue of
events, I will use a synchronization primitive such as a semaphore. You can also read my essay on GUI
Thread Techniques.

Typically, I use a worker thread in the background, and it simply polls some state (such as a Boolean) to
determine what it should be doing. For example, to pause a thread, it looks at the Boolean that says
"Paused", which is set when (for example) a checkbox is set:

// thread body:
while(running)
{ /* loop */
if(paused)
switch(::WaitForSingleObject(event, time))
{
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
continue;
}
// rest of thread
} /* loop */

The trick of doing the c ontinuefor the timeout means that the thread will regularly poll for the running
flag being clear, simplifying your shutdown of the application. I typically use 1000 for the time, polling once
a second for shutdown.

Why did I do the apparently redundant test of the paused Boolean variable before doing the
::WaitForSingleObject? Wouldn't waiting on the object be sufficient?

Yes, the paused Boolean is an optimization hack. Because I use ::WaitForSingleObject, each pass
through this loop involves a moderately heavy-duty operation to test for continuance. In a high-
performance loop this would introduce a completely unacceptable performance bottleneck. By using a
simple Boolean I can avoid the heavy-duty kernel call when I don't need it. If performance is not an issue,
you can eliminate this extra test.

The code in the main GUI thread that sets these variables is as shown below:

CMyDialog::OnPause()
{
if(paused && c_Pause.GetCheck() != BST_CHECKED)
{ /* resume */
paused = FALSE;
SetEvent(event);
} /* resume */
else
if(!paused && c_Pause.GetCheck() == BST_CHECKED)
{ /* pause */
paused = TRUE;
ResetEvent(event);
} /* pause */
}

where event is a handle from ::CreateEvent. Avoid CEvent--at least the last time I tried it, it was so
unbelievably brain-dead buggy that it was unusable. I haven't checked the VC++ 6.0 implementation, so it
may have been fixed, but the bottom line is that the MFC interface to synchronization primitives has been
deeply suspect, and gains nothing over using the actual primitives.

There's another, slightly more complex, mechanism for doing a thread shutdown without polling, which I
discuss in a later section.

Shutting down a thread from a view or main frame

There is sometimes a problem in shutting down a thread. If you don't do things in the right order, you
could even shut down your GUI thread while the worker thread is still running, which can lead to all sorts
of interesting problems. Interesting, as in the traditional Chinese curse. So here's a method I've used to
shut down a thread a be sure it is shut down before the view is shut down.

First, you must store a pointer to the CWinThreadobject in your view, so declare
CWinThread * myWorkerThread;

in your view. When you create the worker thread, create it as

myWorkerThread = AfxBeginThread(run, this);

You will need this variable to synchronize the shutdown with the view termination.

void CMyView::OnClose()
{
// ... determine if we want to shut down
// ... for example, is the document modified?
// ... if we don't want to shut down the view,
// ... just return

// If we get here, are are closing the view


myWorkerThread->m_bAutoDelete = FALSE;
running = FALSE;
WaitForSingleObject(myWorkerThread->m_hThread, INFINITE);
delete myWorkerThread;
CView::OnClose(); // or whatever the superclass is
}

The only odd thing that appears in the previous function is the saving of the m_bAuthoDelete flag explicitly
to FALSE. This is because the deletion of the CWinThread-derived object can close the handle of the
thread, rendering the subsequent W aitForSingleObjectinvalid. By inhibiting the auto-deletion, we can
wait on the thread handle. We then do the explicit deletion of the CWinThread-derived object ourselves,
since it is now no longer useful.

Special thanks to Charles Doucette for pointing out a flaw in my original article which he found in another essay
by Doug Harrison: there was a race condition; I had previously stored the handle and shut down the thread. But
the auto-delete invalidated the handle which could lead to incorrect behavior.

By storing the handle to a variable, we can then do a WaitForSingleObjecton the thread. The close
operation then blocks until the thread terminates. Once the thread has terminated, we can proceed with
the close by calling our superclass OnClosehandler (in this example, we are a derived class of CView).

There is a caution here: this assumes that the thread will actually terminate "within a reasonable time". If
you have a thread that is blocked on I/O or a synchronization object, you will need to add a timeout
mechanism as I have already described. Note that this prohibits the use of C RITICAL_SECTIONas a
synchronization object since they don't have a timeout capability. If you're blocked on a
CRITICAL_SECTIONyou're stuck forever.

Of course, in the general case you may have several synchronization mechanisms that are necessary to
ensure the thread will terminate within a reasonable period of time. A serious design flaw in the whole
AfxBeginThreadmechanism is that it doesn't allow me to create a CWinThread-derived subclass of my
own which is the object created. In this case, I've sometimes subclassed CWinThreadand bypassed the
AfxBeginThreadby doing my own thread creation inside my subclass, and exporting methods such as
CMyWinThread::shutdownthat do whatever is needed to make the thread shut down cleanly and
quickly.

Thread Shutdown Without Polling (20-Jan-01)

The technique I use that polls every few seconds does have two implications: it makes the thread active
every few seconds, and it sets a limit on responsiveness on a shutdown. The shutdown of the application
becomes limited by the maximum time it takes to get out of the thread-wait operation. There is an
alternative implementation I have also used, which involves using a second Event.

HANDLE ShutdownEvent;

This should be initialized via Cr eateEvent. What I do when I'm using this technique is include it in a class
derived from CW inThread, which makes the thread creation slightly trickier. This is because
AfxBeginThreadalways creates a new CWinThreadobject, but if you need your own CWinThread-
derived class, you can't use A fxBeginThread. The technique shown below generalizes this. Note that if I
wanted to be really general, I would create a templateclass. I leave that as an Exercise For The Reader.

/***********************************************************************
* class CMyThread
***********************************************************************/
class CMyThread : public CWinThread {
public:
CMyThread( );
virtual ~CMyThread( );
static CMyThread * BeginThread(LPVOID p);
void Shutdown( );
enum { Error, Running, Shutdown, Timeout };
protected: // data
HANDLE ShutdownEvent;
HANDLE PauseEvent;
};
/**********************************************************************
* CMyThread::CMyThread
* Inputs:
* AFX_THREADPROC proc: Function to be called
* LPVOID p: Parameter passed to proc
***********************************************************************/
CMyThread::CMyThread(AFX_THREADPROC proc, LPVOID p ) : CWinThread(proc, p)
{
m_bAutoDelete = FALSE;
ShutdownEvent = ::CreateEvent(NULL, // security
TRUE, // manual-reset
FALSE, // not signaled
NULL); // anonymous

PauseEvent = ::CreateEvent(NULL, // security


TRUE, // manual-reset
TRUE, // signaled
NULL); // anonymouse
}

/**********************************************************************
* CMyThread::~CMyThread
**********************************************************************/
CMyThread::~CMyThread( )
{
::CloseHandle(ShutDownEvent);
::CloseHandle(PauseEvent);
}

/*********************************************************************
* CMyThread::BeginThread
* Result: CMyThread *
* Newly-created CMyThread object
*********************************************************************/
CMyThread * CMyThread::BeginThread(AFX_THREADPROC proc, LPVOID p)
{
CMyThread * thread = new CMyThread(proc, p);
if(!thread->CreateThread( ))
{ /* failed */
delete thread;
return NULL;
} /* failed */
return thread;
}
/*********************************************************************
* CMyThread::Wait
* Result: DWORD
* WAIT_OBJECT_0 if shutting down
* WAIT_OBJECT_0+1 if not paused
* Notes:
* The shutdown *must* be the 0th element, since the normal
* return from an unpaused event will be the lowest value OTHER
* than the shutdown index
*********************************************************************/
DWORD CMyThread::Wait( )
{
HANDLE objects[2];
objects[0] = ShutdownEvent;
objects[1] = PauseEvent;
DWORD result = ::WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch(result)
{ /* result */
case WAIT_TIMEOUT:
return Timeout;
case WAIT_OBJECT_0:
return Shutdown;
case WAIT_OBJECT_0 + 1:
return Running;
default:
ASSERT(FALSE); // unknown error
return Error;
} /* result */
}

/********************************************************************
* CMyThread::Shutdown
* Effect:
* Sets the shutdown event, then waits for the thread to shut
* down
********************************************************************/
void CMyThread::Shutdown( )
{
SetEvent(ShutdownEvent);
::WaitForSingleObject(m_hThread, INFINITE);
}

Note that I don't make provision here for the full set of options for CreateThread, since the threads I
create do not need flags, stack size, or security attributes; you would need to make the obvious extensions
if you need these features.

To call it from an application, I do something like the following. In the declaration of the class in which the
thread will run, such as a view class, I add declarations like

CMyThread * thread; // worker thread


static UINT MyComputation(LPVOID me);
void ComputeLikeCrazy( );

Then I add methods, such as this one that responds to a menu item or pushbutton in a view:

void CMyView::OnComputationRequest( )
{
thread = <A href="#CMyThread::BeginThread">CMyThread::BeginThread</A>(MyComputation,
this);
}

UINT CMyView::MyComputation(LPVOID me) // static method!


{
CMyView * self = (CMyView *)me;
self->

The code below then shows how I implement a "pause" capability. Alternatively, the P auseEventvariable
could represent a Semaphore on a queue or some other synchronization mechanism. Note, however, that
it is more complex if you want to wait for a semaphore, a shutdown, or a pause. In this case, because you
can only wait on "or" or "and" of the events, and not more complex relationships, you will probably need to
nest two Wa itForMultipleObjects, one for the semaphore-or-shutdown combination and one for the
pause-or-shutdown combination. Although I don't show it below, you can additionally combine this
technique with a timeout. Note that in the example below, the running flag is actually local, rather than
being a class member variable, and is implicitly handled by the case decoding the Sh utdownEvent.

void <A name=CMyView::ComputeLikeCrazy>CMyView::ComputeLikeCrazy</A>( )


{
BOOL running = TRUE;

while(running)
{ /* loop */
DWORD result = thread->

Note that I make provision for a timeout case, even though the current implementation does not provide
for it (an Exercise For The Reader).

Synchronization

Any time you have state shared between two threads it is essential that you provide synchronization on
accesses. I discuss a fair amount of this in our book, Win32 Programming, and don't plan to replicate that
discussion here. What is odd, however, is the fact that for variables such as paused I don't provide any
synchronization. How can I get away with this?

The answer is that synchronization is not required providing only one thread ever modifies the variable, at
least in some restricted cases. In our examples, the main GUI thread modifies the variable paused, but all
other threads, such as the worker thread, only read it. It is true that the worker thread might, by a single
instruction, miss detecting it, but the idea here is that one additional loop of the worker thread won't
matter anyway, because the user might have missed it by tens or hundreds of milliseconds.

It has been pointed out to me that even if only one thread modifies the variable (although several threads
may use it), if it takes more than one instruction (or one memory cycle) to do it, synchronization is
required. For example, if the value is a 64-bit value and two 32-bit instructions are used to store it
(because you did not compile for a native Pentium instruction set), you could have the modifying thread
preempted after it has stored the first 32 bits (whichever, high or low, the compiler has chosen to do first)
but not the second 32 bits. This is, in fact, correct. If you are modifying a scalar value of more than 32 bits,
and the generated code requires more than two instructions to store the value, you must still do
synchronization between the modifier and users of the value to ensure that you have not been victimized
by this anomaly. Note that if the compiler generates a 64-bit store, there might not a be a problem. The
Pentium bus is 64 bits wide, and synchronization is done at the hardware level. But if the value is not
aligned so that a single memory cycle can store it (for example, a 32-bit value split across a 64-bit
boundary), two memory cycles are required to complete the store, making it risky for a multiprocessor
environment. Therefore, you should be careful about taking advantage of this feature. A Tip of the
Flounder Fin to Chris Bond for pointing this out.

So what about that case where I set running to be T RUEbefore AfxBeginThreadand set it FALSEjust as
the thread exited? That just violated my previous statement, didn't it? Well, yes, But note that in this case
the synchronization still exists. The thread is terminating, and therefore any computation left to be done in
the thread is about to complete. No other work will be done in the thread. The GUI thread will not start a
new thread until the running flag is FALSE. Unless you've set the m_bAutoDelete member of the
CWinThreadto be FALSE, all cleanup including deleting the object will be handled automatically. So we
can actually "get away with it".

If you want to be totally correct and precise, the only valid solution is to have yet another thread waiting
on your first worker thread, and when the first worker thread completes, the second worker thread starts
up, sets running to F ALSE, and then terminates itself. This is a little clumsy and is essentially overkill, but is
formally correct.

Summary

Working with threads introduces some complications, but compared to the problems of dealing with
PeekMessageis a much better generalization of the notion of parallel computation. The amount of care
that has to be exercised is startling at first, but after you've done a few multithreaded applications it
becomes almost a reflex. Learn to use threads. You will be better off in the long run.

History
(27-Jan-01) In response to several questions about ResumeThread, I've expanded my discussion of this
topic.

(27-Jan-01) I've added, in response to some issues in the newsgroup, a discussion of how to wait for a
thread to start up.

(20-Jan-01) An alternative mechanism for doing thread shutdown--specifically, how you detect within
the thread it is being shut down--is now documented (the method I wished I'd thought of when I wrote
the chapter in Win32 Programming, and figured out about six months after it went to press). I had meant
to put this in the original essay and forgot about it.

(10-Apr-00) A flaw in the thread shutdown logic was pointed out to me; you have to inhibit auto-
deletion of the CWinThread-derived object explicitly!

A new section on shutting down view-related threads has been added.

(28-Jan-00) The description of pausing and resuming threads has been enhanced with a more detailed
discussion of why SuspendThreadand ResumeThreadmust be avoided.

The views expressed in these essays are those of the author, and in no way represent, nor are they
endorsed by, Microsoft.

Send mail to newcomer@flounder.com with questions or comments about this article.


Copyright © 1999 <!--webbot bot="Substitution" s-variable="CompanyLongName" startspan --
>CompanyLongName<!--webbot bot="Substitution" endspan i-checksum="22147" --> All Rights
Reserved.
www.flounder.com/mvp_tips.htm

License
This article has no explicit license attached to it but may contain usage terms in the article text or the
download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author


Joseph M. Newcomer
United States

No Biography provided

Comments and Discussions


282 messages have been posted for this article Visit
http://www.codeproject.com/Articles/552/Using-Worker-Threads to post and view comments on
this article, or click here to get a print view with messages.

Permalink | Advertise | Privacy | Mobile Article Copyright 2000 by Joseph M. Newcomer


Web02 | 2.8.140322.1 | Last Updated 6 Apr 2001 Everything else Copyright © CodeProject, 1999-2014
Terms of Use

You might also like