Code:
#include <windows.h>
#include <iostream>
#include <cassert>
using namespace std;
// forward decl.
class Thread;
//-----------------------------------------------------------------------------
// ThreadProc_t - Thread function type used by the Thread class. All thread
// functions return DWORD and take a Thread* parameter.
typedef DWORD (WINAPI *ThreadProc_t)(Thread *thread);
class Thread
{
ThreadProc_t m_threadproc;
DWORD m_threadid;
HANDLE m_hthread;
LPVOID m_thread_data;
HANDLE m_evExit;
//-------------------------------------------------------------------------
// Actual thread entry point where we call m_threadproc
static DWORD WINAPI ThreadProc(LPVOID lpv)
{
Thread *t = reinterpret_cast<Thread*>(lpv);
return t->m_threadproc(t);
}//ThreadProc
public:
//-------------------------------------------------------------------------
// enum used by SetPriorityClass() for type safety
enum PriorityClass_t
{
class_normal = NORMAL_PRIORITY_CLASS,
class_idle = IDLE_PRIORITY_CLASS,
class_high = HIGH_PRIORITY_CLASS,
class_realtime = REALTIME_PRIORITY_CLASS,
};//PriorityClass_t
//-------------------------------------------------------------------------
// enum used by SetPriority() for type safety
enum Priority_t
{
priority_above_normal = THREAD_PRIORITY_ABOVE_NORMAL,
priority_below_normal = THREAD_PRIORITY_BELOW_NORMAL,
priority_highest = THREAD_PRIORITY_HIGHEST,
priority_idle = THREAD_PRIORITY_IDLE,
priority_lowest = THREAD_PRIORITY_LOWEST,
priority_normal = THREAD_PRIORITY_NORMAL,
priority_time_critical = THREAD_PRIORITY_TIME_CRITICAL,
};//Priority_t
public:
//-------------------------------------------------------------------------
// Thread constructor.
// threadproc - pointer to function of type ThreadProc_t to execute in
// the context of this thread.
explicit Thread(ThreadProc_t threadproc)
: m_threadproc(threadproc), m_threadid(0), m_hthread(NULL),
m_evExit(NULL), m_thread_data(NULL)
{
assert(m_threadproc != NULL);
m_evExit = ::CreateEvent(NULL, FALSE, FALSE, NULL);
assert(m_evExit != NULL);
}//constructor
//-------------------------------------------------------------------------
// Destructor - If the thread is running, Stop(1000) is called. If still
// running the thread is killed (not good). Make the timeout
// configurable if need be, but I prefer to stop the thread
// myself before allowing the destructor to be called.
virtual ~Thread()
{
if (m_hthread != NULL)
{
Stop(1000); // could make this configurable
if (IsRunning())
Kill();
::CloseHandle(m_hthread);
}//if
::CloseHandle(m_evExit);
}//destructor
//-------------------------------------------------------------------------
// Use these methods to pass custom data to the thread.
void SetThreadData(LPVOID data) {m_thread_data = data;}
LPVOID GetThreadData() {return m_thread_data;}
//-------------------------------------------------------------------------
// HANDLE operator to the thread handle
operator HANDLE() {return m_hthread;}
//-------------------------------------------------------------------------
// Get the thread id of the last thread started by this class.
DWORD GetThreadID() const {return m_threadid;}
//-------------------------------------------------------------------------
// Get the exit event used by this class to notify the thread
// implementation that Stop() was called and should terminate.
HANDLE GetExitEvent() const {return m_evExit;}
//-------------------------------------------------------------------------
// Returns false if the thread hasn't been created. Otherwise, the current
// execution state of thread is returned. Note that this will return true
// if the thread is suspended.
bool IsRunning() const
{
if (m_hthread == NULL)
return false;
return ::WaitForSingleObject(m_hthread, 0) == WAIT_TIMEOUT;
}//IsRunning
//-------------------------------------------------------------------------
// Start the threads execution. Returns true if the thread is already
// running or if the thread was started successfully. Returns false if the
// thread could not be created.
// create_suspended - If true, the thread is created and immediately
// suspended. ResumeThread() must be called to start
// thread execution.
bool Start(bool create_suspended = false)
{
if (IsRunning())
return true;
if (m_hthread != NULL)
::CloseHandle(m_hthread);
::ResetEvent(m_evExit);
DWORD flags = create_suspended ? CREATE_SUSPENDED : 0;
m_hthread = ::CreateThread(NULL, 0, ThreadProc, this,
flags, &m_threadid);
return m_hthread != NULL;
}//Start
//-------------------------------------------------------------------------
// Stop the currently running thread's execution. First the exit event is
// signaled then the thread is waited on for timeout milliseconds. Once
// thread execution stops, it can only be restarted by calling Start(). Use
// SuspendThread() and ResumeThread() to stop a thread's execution and have
// it restart where it left of.
// Returns true if the thread isn't currently running or if the thread
// exits within the given timeout. Use a timeout of INFINITE to wait
// forever for the thread to exit.
// NOTE: The thread implementation must monitor the exit event to be
// notified of when this method is called.
bool Stop(DWORD timeout)
{
if (!IsRunning())
return true;
::SetEvent(m_evExit);
return ::WaitForSingleObject(m_hthread, timeout) == WAIT_OBJECT_0;
}//Stop
//-------------------------------------------------------------------------
// Wait for thread execution to complete. Returns true if the thread
// execution has completed. Returns false if the thread was never started
// or if the timeout occured before thread execution was complete.
// timeout - number of milliseconds to wait before giving up. If not
// specified, an infinite timeout is used.
bool Join(DWORD timeout = INFINITE)
{
if (m_hthread == NULL)
return false;
return ::WaitForSingleObject(m_hthread, timeout) == WAIT_OBJECT_0;
}//Join
//-------------------------------------------------------------------------
// Terminate the currently running thread. Returns true if the thread is
// not running or if the thread was terminated successfully.
// NOTE: This is a dangerous function that should only be used in the most
// extreme cases.
bool Kill()
{
if (!IsRunning())
return true;
return ::TerminateThread(m_hthread, 0xDEADBEAF) == TRUE;
}//Kill
//-------------------------------------------------------------------------
// Get the DWORD return value of the thread function. Returns false if the
// thread was never started or the exit code could not be retrieved.
// exitcode - reference parameter to receive the exit code
bool GetExitCode(DWORD &exitcode)
{
if (m_hthread == NULL)
return false;
return ::GetExitCodeThread(m_hthread, &exitcode) == TRUE;
}//GetExitCode
//-------------------------------------------------------------------------
// Set the priority class of the thread. Returns false if the thread isn't
// running. Returns true if the priority class is set successfully.
// pc = A value from the PriorityClass_t enum
bool SetPriorityClass(PriorityClass_t pc)
{
if (!IsRunning())
return false;
return ::SetPriorityClass(m_hthread, pc) == TRUE;
}//SetPriorityClass
//-------------------------------------------------------------------------
// Set the threads priority. Returns false if the thread isn't running.
// Returns true if the proirity of the thread is set successfully.
// p - A value from the Priority_t enum.
bool SetPriority(Priority_t p)
{
if (!IsRunning())
return false;
return ::SetThreadPriority(m_hthread, p) == TRUE;
}//SetPriority
};//Thread
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Thread class capable of running a non-static class member function in the
// context of a thread. This is achieved by pointer-to-member function
// semantics with the class type as a template argument. In order for a member
// function to be used as a thread, it must return DWORD and take Thread* as a
// parameter.
// OBJ_T - class type of the object containing the member function to be run
// as a thread.
template<class OBJ_T>
class MfnThread : public Thread
{
// type of this class
typedef MfnThread<OBJ_T> MyType;
// pointer to OBJ_T member function type
typedef DWORD (OBJ_T::*MfnThreadFunc_t)(Thread*);
OBJ_T *m_thread_obj;
MfnThreadFunc_t m_pmfn;
//-------------------------------------------------------------------------
// Proxy method used by ThreadProc below.
DWORD RunThread()
{
return (m_thread_obj->*m_pmfn)(this);
}//RunThread
//-------------------------------------------------------------------------
// Thread procedure passed to our base class, simply calls RunThread().
static DWORD WINAPI ThreadProc(Thread *t)
{
return static_cast<MyType*>(t)->RunThread();
}//ThreadProc
public:
//-------------------------------------------------------------------------
// MfnThread constructor.
// obj - templated class type containing the method to be run in the
// context of this thread.
// thread_func - pointer of member function of OBJ_T to be run in the
// context of this thread.
explicit MfnThread(OBJ_T *obj, MfnThreadFunc_t thread_func)
: Thread(ThreadProc), m_thread_obj(obj), m_pmfn(thread_func)
{
assert(m_thread_obj != NULL);
assert(m_pmfn != NULL);
}//constructor
};//MfnThread
// Test class for MfnThread
class Foo
{
LONG n;
DWORD thread_method_increment(Thread *t)
{
return InterlockedIncrement(&n);
}//thread_method_increment
DWORD thread_method_decrement(Thread *t)
{
return InterlockedDecrement(&n);
}//thread_method_decrement
public:
Foo() : n(0) {}
DWORD test1(Thread *t)
{
MfnThread<Foo> t1(this, &Foo::thread_method_increment),
t2(this, &Foo::thread_method_increment),
t3(this, &Foo::thread_method_increment),
t4(this, &Foo::thread_method_decrement),
t5(this, &Foo::thread_method_decrement);
// start em all up
t1.Start();
t2.Start();
t3.Start();
t4.Start();
t5.Start();
// wait for em to finish
t1.Join();
t2.Join();
t3.Join();
t4.Join();
t5.Join();
// hopefully, n will be 1
cout << "n is now " << n << endl;
return n;
}//test1
};//Foo
// Test thread function for Thread class
DWORD WINAPI thread_function(Thread *t)
{
for (int n=0; n<3; n++)
{
cout << "Look at me!, I'm a thread with id = " << t->GetThreadID()
<< endl << flush;
Sleep(750);
}//for
return 69;
}//thread1
int main()
{
//-----------------------------------------------------
// Thread class test
Thread t(thread_function);
// start the thread
t.Start();
// wait for it to complete
t.Join();
DWORD exitcode;
t.GetExitCode(exitcode);
cout << "thread_function returned " << exitcode << endl << endl;
//-----------------------------------------------------
// MfnThread class test
Foo f;
f.test1(NULL);
// now call "f.test1()" again but as a thread
MfnThread<Foo> ft(&f, &Foo::test1);
ft.Start();
ft.Join(); // n should be 2 now
ft.GetExitCode(exitcode);
cout << "ft returned " << exitcode << endl;
return 0;
}//main
A link to this thread should awnser some future postings.