Threads can be complicated beasts until you get used to them. They are certainly useful, but at the same
time, they can add a lot of complexity. I have seen several
attemps to provide the ultimate thread wrapper class for Win32, including
MFC's CWinThread and several others on C/C++ Users Journal, to name a
few.
However, I've never been to keen on any of them. I always thought
there had to be a better way. In December 1999, Allen Broadman and Eric
Shaw published an article on CUJ called "Executing a Class Member in
its own thread" that provided a particular solution to the problem. It was very
elegant in some respects and provided much welcome type safety and a lot of
flexibility: It allowed you to create a thread and call any function on any
object instance you wanted, provided the function had a certain signature.
They really had the right idea, but approached it the wrong way.
While it was good and useful, it was unsuitable for any but the most basic use
of threads. The particular problems they had were:
So I took their idea and implemented it in a way that fixed both
of the problems above. It was rather simple: All you had to do was avoid having
the function's signature as a template parameter! This doesn't reduce type
safety at all because even in Broadman's and Shaw's implementation the
function's signature was known, and could simply be deduced from the object's
type.
Some time after writing this, I decided to rethink the approach I
had, which encapsulated everything in a single class, and instead try something
similar to what Broadman and Shaw had, which was to have a special stand-alone
thread creation function that provided automatic type discovery. This meant
splitting the Thread implementation in three different parts:
Thread
class, which wraps a thread HANDLE and ID, but doesResumeThread()
. It also has correctly defined copy constructor andDuplicateHandle()
internally.Yet another advantage of my implementation is that the class
template allows you to provide a thread creation class, so you can plug in your
custom creation function. I supply two classes:
Win32Threader
, which simply calls theCreateThread()
Win32 api.CrtThreader
, which can be used with VC++ C Runtime library,_beginthread()
.To run threads, you can use one of the two RunOnThread()
functions
provided. The first template argument in each one is the type of the Creator class to use, and has
to be provided explicitly at call time (see example below).
The difference between the two functions is that one is for starting threads on
member functions, while the second overload is able to start threads on global standalone functions, or
static member functions. This is done through the use of an adapter class, called GlobalAdaptor.
Using the template is pretty simple, here's an example:
class ThreadedObj {
public:
DWORD some(int a) {
printf ( "In thread: %d\n", a );
return 0;
}
};
DWORD StandAlone ( int b )
{
printf ( "In thread: %d - %d\n", GetCurrentThreadId(), b );
return 0;
}
using namespace Winterdom::Runtime::Threading;
ThreadedObj obj;
Thread thread = RunOnThread<Win32Threader>(obj, &ThreadedObj::some, 8);
// or something like this for a standalone function
// Thread thread = RunOnThread(StandAlone, 8);
thread.WaitOn();
Grab the file here: winterdom_cpp.zip