Click to edit Master text styles
Traditional procedural approach to thread
programming is to create a thread and pass it a function to execute. But a
function operates on data. The data is most likely shared with other threads.
In the object-oriented world data comes first. You create an ActiveObject
whose state develops asynchronously. There is a captive thread inside an
Active Object, but it's only an implementation detail.
I will describe a
C++ implementation of Active Object using Windows API and I will discuss
multiple uses of ActiveObject
in our own commercial product, including
various synchronization techniques.
A similar concept in Java is called the Runnable interface.
There is a basic incompatibility between
the “assembly-line” model and Windows programming. A Windows application must
. An application cannot simply schedule user interactions
at convenient times. User interaction drives
What to do when the user initiates a time-consuming operation?
Stop responding for the duration of the operation. Danger: the user might
decide to kill the non-responding application
Display a progress meter and
update it at convenient intervals. Or even better, give the user the
opportunity to cancel the operation. Inconvenience: the user is supposed to
sit there and gaze at the screen. Danger: the user will start browsing the
Internet and forget about the job.
Let the user continue work, displaying
results incrementally as they arrive. Be fully interactive. That requires
giving the job to a “contractor”—an independent thread of execution.
The application must know when the job is
done. It might also require periodic updates. It may want to modify the
Communicating with a thread is not trivial.
Data may be shared, but access has to be coordinated.
One thread must be able to “kick” another thread:
thread blocks on an event, main thread signals it
thread posts a Windows message for the main thread
The main thread might occasionally poll the worker thread at convenient times.
For instance, set up a Windows timer to do the polling.
When the job is done, the main thread has to know about it.
-It blocks waiting for signal?
-It gets a Windows message
Killing a working thread is like derailing a speeding train
Imagine that each thread executes on a
separate processor. (In most cases, the processor is virtual).
simplest operations on memory (other than local/stack memory) may be
interleaved. Operations like ++i, may consists of
the value of i into register
the value of register into I
Two threads doing the same may interleave and create wrong results.
Windows, the compiler guarantees word-aligned store to be atomic
library functions like InterlockedIncrement, etc., are atomic
sequences protected by critical sections and semaphores
Operating-system primitives to create threads.
Conceptually, associate a new virtual processor with a thread, allocate a
stack, point it at the code to be executed (thread procedure), give it a chunk
of shared data (void pointer) where initial parameters are stored.
How the thread communicates, or terminates, is up to the implementor.
This is how it’s done in Windows. Some
parameters default (security attributes, stack size).
Thread handle is
returned. To be deallocated using CloseHandle. It’s a resource
id is created. Some APIs use thread id to identify the thread (for instance,
in another process).
Thread procedure must have the above signature. It
takes void pointer to client-defined data and returns a status.
Here’s some template magic. Sys::Handle
defines its disposal policy—calling CloseHandle API when deallocated.
Thread::Handle adds a few useful thread-specific methods.
A thread can be suspended and resumed. We will be starting threads in the suspended
state, so we will have to resume them.
Waiting on a thread handle blocks
until the thread terminates (or the timeout elapses).
You can poll the thread for signs of life.
More template magic. AutoHandle is a
smart pointer for handles. The template takes disposal policy as a parameter.
AutoHandle can has move semantics (like auto_ptr).
AutoHandle inherits all
the methods of its base type, in this case Thread::Handle.
Constructor starts a thread.
Destructor closes the thread handle.
Kill derails the speeding train.
The constructor starts a thread.
Notice the use of _beginthreadex
– you are not supposed to use Windows
in Windows programs (go figure!). Here’s what Jeff
Richter told me:
There are not a
lot of C-Runtime functions that require TLS. The best way to know is to examine the C-Runtime
source code which ships with all copies of Visual C++. Certainly strtok, asctime, and
gmtime require it. I also know that errno is a macro that access TLS. So, any function that sets
errno is also using TLS.
just use _beginthreadex instead of CreateThread - period. There is no reason
However, if you use CreateThread AND dynamically-link to the C-Runtime DLL,
then the TLS
will NOT leak. But, this ties the freeing of TLS to your build process which
is always a bad idea.Using _beginthread means that TLS will get freed regardless of how you
build your app.
if the thread is terminated (as opposed to exits) for some reason, the TLS
Thread is a low-level abstraction. In my
opinion (not shared by BOOST) there is no sense in templatizing it. But I am
an object-oriented kinda guy.
Putting data first is the basis of
object-oriented philosophy. Where there’s thread, there’s always a data
structure behind it. So let’s start with a data structure.
This is our “contractor”. It has its
memory (state, data). The client initializes the data. Some of the data is
shared with the client, some is not. The contractor also owns synchronization
primitives that protect its (shared) data.
When designing such an object,
the client defines its data and behavior. The internal behavior is defined by
the implementation of the Run method.
Once the active object is declared,
the client can create an instance, start its internal processing, interact
with it, and then stop it and delete it.
The client never sees the gory details of
thread creation, thread procedure, void pointers, or casting. Thread procedure
is itself a framework. It gets the this pointer and elicits its behavior.
Every active object defines its own
version of the Run method. It has access to all the internal methods
and data of the object. It’s a big step forward from a faceless weakly typed
This is how the gory details are
encapsulated. A thread is born with a standard thread procedure, ThreadEntry,
and the this pointer.
We don’t want the thread to start
immediately, before the construction of the ActiveObject-derived class
is finished. We are passing the this pointer which will be accessed by
the running thread—it must be completely initialized.
The actual application, CmpDir is more
complex, but this is a simplified description.
Lister doesn’t list the
whole directory at once. It splits the work into manageable chunks, so that it
can pass partial results for display. Beats the progress meter!
Accumulator must add up all the chunks.
ListView is used for display. It can be updated by inserting items and by changing
When this Active Object is created, it
blocks on an event. There’s nothing for it to do. It wakes up when the
external producer (the Lister) delivers new data (or when the client calls
FlushThread). MergeChunks is where the work is done. Although not shown here,
the result of MergeChunks is passed to the external sink object. But when the
client wants to kill the Active Object, it detaches the sink and sets the
isDying flag. So MergeChunks might find itself with a null sink. It should
either build a critical section around the sink or simply accept the GP-fault
(it will be caught anyway).
There still is too much of a burden on
the user of ActiveObject. He has to start the thread (it is created suspended)
and kill the thread before deleting ActiveObject.
Kill does the following:
the _isDying flag (atomic operation in Windows)
the thread, in case it is blocked on an event (after being released from a
wait, the thread should always check the IsDying flag)
the kill event. This is an event that the thread is waiting for after it has
completed its task
Optionally, wait for the thread to actually terminate
(default timeout should be zero)
During destruction the vtable of the
object gets substituted. The code inside the destructor of the outer object
(derived class) may call its virtual functions—the vtable is that of the
derived class. But when the destructor of the base object is called, the
vtable of the base class is used (conceptually at least), even if the call was
made implicitly from the destructor of the derived object. You cannot have a
Here’s the snag. The client cannot delete
the object while the thread is still running (it will most likely fault trying
to access its data structures). So should the client wait for death after
killing it? That could take arbitrarily long! Defeats the purpose of
We sort of want the active object to be garbage collected.
The best solution is for the thread to delete the object in the final throes
Warning: not too soon! If the thread completes its task, it
shouldn’t immediately delete the object, since the client might still try to
access it. It has to wait for the client to kill it.
The ownership of the
object must be transferred from the client who creates it, to the thread who
Active Object is held inside an auto_ptr, to insure its deletion.
Notice the wait for external kill command.
Active object contains a kill event to
synchronize the exit of the thread and the deletion of the object.
The calls to ResumeThread and Kill are
not optional. Making them client’s responsibility is asking for trouble. Also,
we don’t want the client to call delete.
Here’s the Lister example again.
A new Lister is allocated on the heap and immediately passed to auto_active, which
activates it and takes care of ownership transfer, killing, and garbage collection.
Lister creates a file sequencer to list
files. The results are stored in a Data::Chunk. The inner loop fills the chunk
with maximum MAX_FILES_TO_LIST at a time and passes it to the sink. The outer
loop iterates until the whole directory is listed, after which Lister is done.
It will then wait for the kill from the client.
Lister may be interrupted
prematurely by the client, in which case the IsDying predicate returns true
and the thread exits.
1.The constructor starts
the thread. Note: we should check if the thread really started running,
otherwise we should delete the active object (the ownership transfer hasn’t
2.The destructor kills
the Active Object (it will still run for a moment and then delete itself)
3.The timeout may be
changed using a separate method
checks if the thread is still running
is used to initialize auto_active outside of the constructor
smart pointer access methods
to set the timeout before killing the object
The client, which in our case is the
Lister, passes a chunk of data to the Accumulator active object. This method
is executed by an external thread (Lister thread). The data, in this case the
_sources vector, must be protected by a critical section.
after the event is released, the Run method of Accumulator wakes up and starts
processing data, trying to access _sources. If the lock is still held by
DataReady, it will block and wait until DataReady returns and the destructor
of lock exits the critical section.
We are now looking from the perspective
of the Lister. It pokes the Accumulator by calling its DataReady method. From
the point of view of the Lister, the Accumulator is nothing but a DataSink (an
Window Procedure handles all Windows
messages. It must handle them quickly, otherwise the application appears
The top-level sink must somehow interrupt
the application to tell it that data is ready. Here the Controller object,
which is the handler of Windows messages in an object-oriented implementation
of Window Procedure, acts as a top-level sink. It posts a user-defined
message, which will be picked by the main thread at the more opportune moment.
Notice, this method is executed by a worker thread, not the main Windows
thread. Access to the message queue is synchronized by Windows.
class MyController: public Win::Controller, public Data::Sink
If (message is ‘data ready’)
display partial data