Simple Thread Class | CodeGuru

Simple Thread Class

Creating a thread is simple: You just call ::AfxBeginThread(…), supply a thread handler and voila, thread is started. No matter if you start a thread in a suspended or not suspended state, you know when the thread is started. However, thread destruction is a different story. In all but trivial applications, you have at least […]

Written By
CodeGuru Staff
CodeGuru Staff
Aug 6, 1998
2 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More


Creating a thread is simple: You just call ::AfxBeginThread(…),
supply a thread handler and voila, thread is started. No matter if you
start a thread in a suspended or not suspended state, you know when the
thread is started. However, thread destruction is a different story.

In all but trivial applications, you have at least one thread which
accesses data available also to other parts of the program or other threads.
Acess to data should be synchronized using any of the Win32 available synchronization
API. When destroying the thread, you should know when a thread stopped
its execution so that you can destroy data structures (otherwise, you get
an exception).

Class TBaseThread is a simple class which encapsulates all code related
to thread creation and its destruction. This is an abstract class – you
have to derive a new class for each thread in your application. Reason
for this is a thread handler – it is a virtual abstract function in a base
class.

Include file:

class TBaseThread {
    protected:
        HANDLE EKillThread;           // When set, thread will be killed
        HANDLE EThreadIsDead;         // Thread sets to indicate thread is finished
    private:
        CWinThread *Thread;
    public:
        static UINT __cdecl ThreadFunc(LPVOID);
    public:
        TBaseThread();
        virtual ~TBaseThread();
        virtual void Create(int mode = 0);    // Start immediately
        virtual void Destroy(void);
        void DoExecute(void);
        virtual void Execute(void) = 0;       // Derived classes will supply implementation
        virtual void Suspend(void);
        virtual void Resume(void);
};

Source code file:

 

TBaseThread::TBaseThread()
{
    Thread = NULL;
}
TBaseThread::~TBaseThread()
{
    DWORD exitCode;
    if (Thread != NULL && ::GetExitCodeThread(Thread->m_hThread, &exitCode) && exitCode == STILL_ACTIVE)
        Destroy();
}
// Mode argument specifies whether thread is created in a suspended state or not.
// = 0 to start thread immediatelly or = CREATE_SUSPENDED to create a thread in suspended state.
void TBaseThread::Create(int mode /* = 0 *)
{
    if (!Thread) {
        // Auto reset, initially reset
        EKillThread = ::CreateEvent(NULL,FALSE,FALSE,NULL);
        EThreadIsDead = ::CreateEvent(NULL,FALSE,FALSE,NULL);
        // Create the thread.
        Thread = ::AfxBeginThread(ThreadFunc,this,THREAD_PRIORITY_NORMAL,0,mode);
    }
}
void TBaseThread::Destroy(void)
{
    if (Thread) {
        // Tell thread to commit suicide
        BOOL retcode = ::SetEvent(EKillThread);
        if (!retcode)
            TRACE0("SetEvent(EKillThread) in TBaseThread::Destroy() returned errorn");
        // Wait for info that thread is killed
        if (::WaitForSingleObject(EThreadIsDead,10000L) == WAIT_TIMEOUT) {
            // Safe guard - if something is wrong then brutally kill thread
            ::TerminateThread(Thread->m_hThread,1);
        }
        // Wait a bit
        ::Sleep(10);
        // Maybe not necessary, but just in case
        Thread = NULL;
    }
    ::CloseHandle(EKillThread);
    ::CloseHandle(EThreadIsDead);
}
// Thread handler
UINT TBaseThread::ThreadFunc(LPVOID arg)
{
    TBaseThread *thread = (TBaseThread*)arg;
    thread->DoExecute();
    return 0;
}
void TBaseThread::DoExecute(void)
{
    Execute();        // Abstract virtual function
    ::SetEvent(EThreadIsDead);
}
void TBaseThread::Suspend(void)
{
    if (Thread)
        Thread->SuspendThread();
}
void TBaseThread::Resume(void)
{
    if (Thread)
        Thread->ResumeThread();
}

How to use it:

First, you have to derive a new class representing your thread (let’s
call it TMyThread). The only function that you must implement is Execute().
This is the actual thread handler which has one of the following 3 forms:

Simple situation. Thread sleeps for 100ms, then
it wakes up, processes whatever is needed and goes back to sleep. If it
is woken up by local TBaseThread::EKillThread  event, it exits.

// Thread handler number 1.
void TMyThread::Execute(void)
{
    DWORD retcode;
    while (TRUE) {
        // EKillThread is a protected data member of TBaseThread.
        retcode = ::WaitForSingleObject(EKillThread,100L);
        if (retcode == WAIT_OBJECT_0) {
            break;    // Thread owner signaled this thread to exit
        } else if (retcode == WAIT_TIMEOUT) {
            // Process data - thread specific code
        }
    }
}

More complex situation. Thread sleeps for 500ms.
It can wake up for a number of reasons.


    o If it is a timeout, execute
thread default action for timeout or do nothing


    o If it is a signalled ESave
or ETrigger event (just an example), handle actions for these events.


ESave or ETrigger event can be locally created
(in derived virtual Create() member function) or specified via TMyThread
constructor.

// Thread handler number 2
void TMyThread::Execute(void)
{
  HANDLE handles[3];
  DWORD whichEvent;
  handles[0] = EKillThread;        // Protected data member of TBaseThread
  handles[1] = ESave;              // Application specific handle to event
  handles[2] = ETrigger;           // Application specific handle to event
  while (TRUE) {
    whichEvent = ::WaitForMultipleObjects(3,handles,FALSE,500L);
    if (whichEvent == WAIT_TIMEOUT) {
      // Timeout - add code
    } else if (whichEvent == (WAIT_OBJECT_0 + 0)) {
      // EKillThread signaled - thread owner requests thread termination
      break;    // Exit thread loop
    } else if (whichEvent == (WAIT_OBJECT_0 + 1)) {
      // Application specific ESave event is signalled - do whatever
    } else if (whichEvent == (WAIT_OBJECT_0 + 2)) {
      // Application specific ETrigger event is signalled - do whatever
    }
  }
}

In this case, thread executes its job and immediately
exits. TBaseThread::Destroy() does not wait since EThreadIsDead is already
in signalled state (done by TBaseThread::DoExecute() function)..

// Thread handler number 3.
void TMyThread::Execute(void)
{
    // do something and immediately exit
}

Declare an object of TMyThread class either in CWinApp derived class or
your CDocument derived class. To start a thread, execute Create() member
function specifying initial start mode (0 to start immediatelly or CREATE_SUSPENDED
to create a thread in suspended state). To destroy a thread, execute Destroy()
member function – this can take time to execute depending on your thread
handle.

 

Last updated: 17 May 1998.

CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.