Introduction
The C++ language invites for object oriented programming. The Win32 API is entirely based on the C programming language. Writing software for the Windows platform always requires the use of the Win32 API. Many developers who prefer C++ and object oriented programming would wish to have available appropriate C++ class libraries, to give their software a consistent object oriented look and feel.
The immense popularity of Java, and now .NET, is mostly based on the large number of classes that in fact make up the programming platform. Java and .NET application programmers just simply write their applications utilizing these classes whereas, by contrast, C++ programmers first write an infrastructure and then use it to write the applications. In this article, I will show you how to write a simple C++ class that wraps the Win32 thread related APIs.
Class Thread
The Java and .NET platform already have proposed some very good models and so we might as well make our model look similar. The advantage of it is that anyone familiar with Java or .NET can easily relate to it.
The threading models in Java as well as in .NET require that a thread object accepts a class method as its thread procedure. Here is an illustration:
Threading in Java
public class MyObject implements Runnable {
public void run() {
}
}
MyObject obj = new MyObject();
Thread thread = new Thread(obj);
tread.start();
Threading in .NET
public class MyObject {
public void Run() {
}
}
MyObject obj = new MyObject();
Thread thread = new Thread(new ThreadStart(obj.Run));
tread.Start();
The models are remarkably similar. Java requires the threadable object to implement the Runnable
interface, and .NET, in a way, requires the same thing because the Thread
classes on either platform expects a threadable procedure to be of this form: public void run()
.
The Java specification is rather simple. Just one simple interface exposing one simple method. The .NET specification is more sophisticated. The 'delegate' concept lends greater flexibility to the writing of multi-threaded programs. Here is an illustration:
public class MyObject {
public void ThreadProc1() {
}
public void ThreadProc2() {
}
}
MyObject obj = new MyObject();
Thread thread1 = new Thread( new ThreadStart(obj.ThreadProc1) );
thread1.Start();
Thread thread2 = new Thread( new ThreadStart(obj.ThreadProc2) );
thread2.Start();
The .NET threading model offers more advantages. Any class method that is compatible with the ThreadStart
delegate can be run as a thread procedure. And as the code snippet above illustrates, a single object instance can concurrently be accessed and manipulated by multiple threads. This is a very powerful feature.
We naturally prefer a C++ threading model to be as simple as that of Java and as flexible as that of .NET. Let us focus first on the Java-like simplicity. Here is a proposal:
struct IRunnable {
virtual void run() = 0;
};
class Thread {
public:
Thread(IRunnable *ptr) {
_threadObj = ptr;
}
void start() {
DWORD threadID;
::CreateThread(0, 0, threadProc, _threadObj, 0, &threadID);
}
protected:
IRunnable *_threadObj;
static unsigned long __stdcall threadProc(void* ptr) {
((IRunnable*)ptr)->run();
return 0;
}
};
We can now write a multi-threaded program as elegantly as the Java folks can do.
class MyObject : IRunnable {
public:
virtual void run() {
}
}
MyObject *obj = new MyObject();
Thread thread = new Thread(obj);
tread->start();
It is so simple because we have buried the Win32 API call into a wrapper class. The neat trick here is the static method defined as part of our Thread
class. We have thus emulated the simpler Java Thread
class.
The .NET Thread
and ThreadStart
approach is a little harder to emulate. But we can still realize it in a way by using pointers to class methods. Here is the example:
class MyObject : IRunnable {
typedef void (MyObject::* PROC)();
PROC fp;
void threadProc1() {
}
void threadProc2() {
}
public:
MyObject() {
fp = threadProc1;
}
void setThreadProc(int n) {
if(n == 1)
fp = threadProc1;
else
if(n == 2)
fp = threadProc2;
}
virtual void run() {
(this->*fp)();
}
};
MyObject *obj = new MyObject();
obj->setThreadProc(1);
Thread *thread1 = new Thread(obj);
thread1->start();
obj->setThreadProc(2);
Thread *thread2 = new Thread(obj);
thread2->start();
The actual threadable method run()
now uses a pointer to a class method to run the appropriate thread procedure. That pointer must be correctly initialized before a new thread is started.
Conclusion
Wrapping the Win32 APIs into C++ classes is the preferred practice. The Java and .NET platforms provide us with well defined models. And by comparison, these models are so similar that defining C++ classes for a thread class, socket class, stream class, etc. should just be a matter of following the provided documentation.
You may download the Thread
class and try it out. I have designed it to be as simple as possible but you may enhance it by wrapping an additional number of thread related APIs, e.g. SetThreadPriority, GetThreadPriority
etc.