‹header›
‹date/time›
Click to edit Master text styles
Second level
Third level
Fourth level
Fifth level
‹footer›
‹#›
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 be responsive. An application cannot simply schedule user interactions at convenient times. User interaction drives the program.
What to do when the user initiates a time-consuming operation?
-Freeze: 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 job.
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:
-Worker thread blocks on an event, main thread signals it
-Worker 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). Even the simplest operations on memory (other than local/stack memory) may be interleaved. Operations like ++i, may consists of
-Fetch the value of i into register
-Increment register
-Store the value of register into I
Two threads doing the same may interleave and create wrong results.
Synchronization primitives
-In Windows, the compiler guarantees word-aligned store to be atomic
-Special library functions like InterlockedIncrement, etc., are atomic
-Longer 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. Thread 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 API CreateThread 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.

You should just use _beginthreadex instead of CreateThread - period. There is no reason not to. 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.
Of course, if the thread is terminated (as opposed to exits) for some reason, the TLS will leak.

--Jeff
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 thread procedure.
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 their appearance.
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:
-Sets the _isDying flag (atomic operation in Windows)
-Disconnects external sinks
-Flushes the thread, in case it is blocked on an event (after being released from a wait, the thread should always check the IsDying flag) -Release 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 “framework” destructor.
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 multithreading.
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 of dying. 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 deletes it.
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 completed)
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
-IsAlive checks if the thread is still running
-reset is used to initialize auto_active outside of the constructor
-Standard smart pointer access methods
-Possibility 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. Immediately 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 interface).
Window Procedure handles all Windows messages. It must handle them quickly, otherwise the application appears non-responsive.
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
MyController::OnUserMessage (…)
If (message is ‘data ready’)
    display partial data