![]() |
General Programming »
Threads, Processes & IPC »
Threads
Intermediate
Effective Threads in C++ - Part 1: The Basic Thread ClassBy FlamTapsWrapping the Win32 Thread API into a C++-friendly class. |
VC6, VC7, VC7.1, VC8.0, Windows, Visual Studio, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
Over the past few years, multithreaded apps have become the mainstay of my development repertoire. I've even found myself writing multithreaded apps where it wasn't needed. Worker threads allow Win32 applications to improve the user experience and increase performance.
For C++ programs and programmers, the Windows API's C interface doesn't necessarily fit well into new or existing class hierarchies. However, the thread concept has become the mainstay of parallel programming practice, and has become more appropriate in a wider and wider variety of circumstances.
In part 1 of this three article series, I will present my general thread class, explaining the design decisions behind it and how to use it to get the greatest effect.
Before we get ahead of ourselves, let's take a look at the public interface of the Thread class:
class Thread { public: Thread(); virtual ~Thread(); // Suspend - Suspends the thread (if one is active) void Suspend(); // Resume - Resumes a previously suspended thread void Resume(); // Terminate - Terminates the thread (if one is active). // Prefer another means of exiting the thread, as // calling Terminate() does not allow the thread to free // any resources it may hold void Terminate(); // IsThreadActive - Called in the context of another // (external) thread to test whether the thread is // currently running bool IsThreadActive() const; };
This rather basic interface is also rather clean. It exposes a fair amount of the operations the Win32 API allows on threads themselves, in a clean, C++-friendly class.
Now, you may be saying to yourself, "Hey? You said it was a complete interface. There isn't even a way to create a thread in there!". Ahh, we're getting there. But before we do, we have to go through template land.
That�s right, my implementation of the Thread class uses a little template-magic. This magic is needed to allow us to use the same code among different client classes. After all, you wouldn't want to re-write the class for each client, would you?
So, let's look a little closer at a few particular lines of the source code.
template<class T, class P> class Thread { public: typedef void (T::*ThreadFunc)( P ); // ... // Same as above // ... };
There are actually two different template parameters: T and P; or, put another way: Class and Parameter.
To invoke a method on an object, the compiler needs two things: the object's type (the T in the template argument list), and the method's signature (which is composed of the return type, class, method name, and parameter list). For this implementation, the method is assumed to return nothing (void) and takes one parameter (see article #3 in this series for a more general solution which doesn't make these assumptions).
The typedef makes things very easy. It is a method typedef, specifying that ThreadFunc is a function pointer to a function of T which takes a P as a parameter and returns nothing. See why the typedef makes it easier?
Now that we've pushed and shoved our way through template land, let's look at the meat of the Thread class: the Run() method.
// Run - Start the Thread and run the method // pClass->(*pfFunc), passing p as an argument. // Returns true if the thread was created // successfully, false otherwise bool Run( T* pClass, ThreadFunc pfFunc, P p );
That's it! That's the public interface for the Thread class. Nothing too intense. And the implementation is not much more complex. So, with that, how do we use this new class?
Let's look at an example on how to use the Thread class:
class TestClass { ... private: Lib::Thread<TestClass, int> m_thread; };
In this example, the Thread class should be used as a data member of another class (with a little modification, the class can be used as a stand-alone object; I will leave that as the dreaded and hated exercise to the reader............ for now). The enclosing class is the first template parameter to the Thread class. This is needed for the compiler to invoke the correct method of the correct class. The second template parameter is the type of argument that will be passed to the thread method.
To start the thread, call the Thread::Run() method:
m_thread.Run( this, &TestClass::DoWork, i );
This will create a new thread using the standard Win32 ::CreateThread(...) method. This new thread will immediately turn around and call the method specified to the Thread::Run() method (I know it may not look like it, but we're specifying a function in all those arguments).
The three arguments to the Thread::Run() method are:
What do we need to specify a function call? From the compiler's perspective, they are return type, class, method name, and parameter list. From the runtime's perspective, we also need an object. Since the return type is hard coded, and the class and parameter list is specified in the template argument, all the compiler needs is a method name, and the runtime needs an object. Guess what? Those are the first two parameters of the Thread::Run() method (parameter 2 and 1, respectively).
After the thread is called, it (essentially) turns around and calls:
this->DoWork( i );
As soon as this method exits, the thread is finished with its work, and it exits as well.
The Thread class is rather basic, but very useful. It provides a C++ wrapper for the powerful Win32 C Threading API. This particular implementation makes a few assumptions: the Thread class must be a member of an enclosing class, the return type must be void, and the method takes a single parameter.
These assumptions may be less than ideal for the time, so article #3 will show you how to get around these assumptions using more template magic. (Trust me, it won't be that painful.)
But first, article #2 will show you how to extend the Thread class into the most useful threading paradigm I have ever come across.
| You must Sign In to use this message board. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 22 Nov 2006 Editor: Smitha Vijayan |
Copyright 2006 by FlamTaps Everything else Copyright © CodeProject, 1999-2009 Web09 | Advertise on the Code Project |