Click here to Skip to main content
16,015,481 members
Articles / Programming Languages / C++
Article

Win32 Worker Thread Wrapper

Rate me:
Please Sign up or sign in to vote.
4.45/5 (10 votes)
18 Apr 2003Apache2 min read 123.7K   2.1K   27   26
Win32 Worker Thread Wrapper

Sample Image - demo.jpg

Introduction

Windows is an OS that does a "great job" of pretending to be a multitasking environment, so it would be a shame, if we programmers would not use this feature at least just for fun. This class should bring you this fun, and maybe (I hope) some knowledge. The class CAG_Thread is just another win32 worker thread wrapper class. Yes, I am sure you could find many other classes that would do the same job just as fine or even better, but what I tried to accomplish is the simplicity. But as we all know the simplicity is subjective, so I will let you judge it for yourself.

Background

Some time ago I noticed that every once in a while I write a worker thread. Each time I need to write one, I copy and paste some functions, change them, and adjust them so they do the new job. After a while I got bored so I decided to write a class that would be very simple and still provide most common features used by me in a worker thread. So the class CAG_Thread was born.

Using the code

The class should be very simple to use. It is intended to be a base class for another class, because it contains the pure virtual function ThreadMain() which must be overridden. Here is the step by step use of the class:

  1. Include AG_Thread.cpp and AG_Thread.h into your project
  2. Derive a new class from CAG_Thread class
  3. Override in the new class the virtual function ThreadMain()
  4. Use the newly created class to instantiate and start the thread by StartThread() function

Here is more detailed explanation with sample code which could be downloaded at the top of the article. I will assume that you should know how to add files to project. Follwoing is the second step of deriving a new class, in my case myThread class from CAG_Thread class. Following is the myThread class definition.

#include "AG_Thread.h" 

class myThread : public CAG_Thread
{
public:
 myThread();
 virtual ~myThread(); 

 // I provide this function to pass any kind of parameters
 BOOL Work(CProgressCtrl *pProgress); 

protected:
 // Progress control to show progress
 CProgressCtrl *m_pProgress; 

 // must override this function
 UINT ThreadMain();
}; 

And here is (partly) the  class implementation

BOOL myThread::Work(CProgressCtrl *pProgress)
{
 if(pProgress == NULL)
  return FALSE; 

 m_pProgress = pProgress; 

 return StartThread();
} 

UINT myThread::ThreadMain()
{
 // init progress control
 m_pProgress->SetRange(0, 100);
 m_pProgress->SetPos(0); 

 for(int i=0; i<101; ++i)
 {
  if(ShouldDie())
   return 1; 

  m_pProgress->SetPos(i);
  Sleep(50);
 } 

 return 0;
}

Now we need to create instance of our class myThread in the dialog box and use it. I simply used a button for starting the thread, but it is not limited to dialog boxes. So here is the function where the starting and ending of the thread occurs.

// check is the thread running and if not then
// start it otherwise abort it
if(m_myThread.GetThreadStatus())
{
 if(m_myThread.StopThread())
 {
  AfxMessageBox(_T("Thread killed successfully!"));
  SetDlgItemText(IDC_START_STOP_BUTTON, _T("Start Thread"));
  return;
 } 

 AfxMessageBox(_T("Could not abort thread."));
}
else
{
 if(m_myThread.Work(&m_progress))
 {
  SetDlgItemText(IDC_START_STOP_BUTTON, _T("Stop Thread"));
  return;
 } 

 AfxMessageBox(_T("Could not start thread."));
}

And that should be it. The demo included is the very basic example of what this class is good for. Please note that this class does not provide any synchronization objects, with the purpose to be as simple as possible. Also the demo is not a useful application, it simply shows the principal of using the class. This article is not very detailed, which again should add to the simplicity, but the code should be documented enough. English is my third language, so if you do not understand something (and experience tells me that you will not), please leave a message and I'll try to answer.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
President Infinity Software Solutions, LLC.
United States United States
Originally from Bosnia and Herzegovina, but lived for 6 years in Germany where I did majority of education, then moved to US, where I live since 1999. I like programming, computers in general, but also Basketball, Soccer, Tennis, and many other things. Masters graduate from Grand Valley State University in CIS and working as a full time software developer. Please visit my website www.amergerzic.com

Comments and Discussions

 
Questionnice work Pin
smart_dummies18-Feb-14 19:09
smart_dummies18-Feb-14 19:09 
GeneralCool stuff Pin
Nedim Sabic23-Nov-08 23:44
Nedim Sabic23-Nov-08 23:44 
GeneralRe: Cool stuff Pin
Amer Gerzic24-Nov-08 2:53
Amer Gerzic24-Nov-08 2:53 
GeneralAn alternative view Pin
Mizan Rahman21-Jul-08 1:39
Mizan Rahman21-Jul-08 1:39 
GeneralRe: An alternative view Pin
Amer Gerzic21-Jul-08 2:33
Amer Gerzic21-Jul-08 2:33 
Questionwhat about function for chagin thread priority Pin
Member 38131324-Sep-03 1:36
Member 38131324-Sep-03 1:36 
AnswerRe: what about function for chagin thread priority Pin
Amer Gerzic24-Sep-03 2:22
Amer Gerzic24-Sep-03 2:22 
GeneralRe: what about function for chagin thread priority Pin
Member 38131324-Sep-03 4:32
Member 38131324-Sep-03 4:32 
GeneralRe: what about function for chagin thread priority Pin
Amer Gerzic24-Sep-03 7:46
Amer Gerzic24-Sep-03 7:46 
QuestionHow can i check that the thread is done ? Pin
Sickboy17-Sep-03 10:54
Sickboy17-Sep-03 10:54 
AnswerRe: How can i check that the thread is done ? Pin
Amer Gerzic18-Sep-03 1:59
Amer Gerzic18-Sep-03 1:59 
GeneralRe: How can i check that the thread is done ? Pin
Sickboy18-Sep-03 7:08
Sickboy18-Sep-03 7:08 
GeneralRe: How can i check that the thread is done ? Pin
Amer Gerzic18-Sep-03 9:47
Amer Gerzic18-Sep-03 9:47 
GeneralInteresting! Pin
WREY30-Apr-03 14:06
WREY30-Apr-03 14:06 
GeneralRe: Interesting! Pin
Amer Gerzic1-May-03 1:54
Amer Gerzic1-May-03 1:54 
GeneralRe: Interesting! Pin
WREY1-May-03 11:13
WREY1-May-03 11:13 
GeneralRe: Interesting! Pin
Amer Gerzic2-May-03 1:44
Amer Gerzic2-May-03 1:44 
GeneralCThread by Bjarke Viksoe Pin
Stanislav Panasik20-Apr-03 19:02
Stanislav Panasik20-Apr-03 19:02 
Hello!

I read your article and want to point you to another simple imlementation
of the worker thread - by Bjarke Viksoe.
This is just a same code in some points, but has a some more useful moments.
I never know when i begin a new piece of work - will i use CRT code ?
Using a compile time checking is a good thing for me, so look at the Create method and you will see what i say about.

The other code by Bjarke you can find at viksoe.dk, i just include all his CThread code for speed.

I use this code in many places...

Thanks for article ! Big Grin | :-D

Stanislav.


#if !defined(AFX_THREAD_H__20000927_7992_09B2_A0EF_0080AD509054__INCLUDED_)
#define AFX_THREAD_H__20000927_7992_09B2_A0EF_0080AD509054__INCLUDED_

#pragma once

/////////////////////////////////////////////////////////////////////////////
// Thread - wrapper for Thread API
//
// Written by Bjarke Viksoe (bjarke@viksoe.dk)
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Beware of bugs.
//

#if defined(_MT) || defined(_DLL)
// Due to the nature of the multithreaded C runtime lib we
// need to use _beginthreadex() and _endthreadex() instead
// of CreateThread() and ExitThread(). See Q104641.
#include <process.h>
#endif

#ifndef ATLTRY
#define (x)
#endif


/////////////////////////////////////////////////////////////////////////////
// CThread

template<bool t_bmanaged="">
class CThreadT
{
public:
HANDLE m_hThread;
bool m_bSuspended;

CThreadT() : m_hThread(NULL), m_bSuspended(false)
{
}
virtual ~CThreadT()
{
if( t_bManaged ) Release();
}
BOOL Create(LPTHREAD_START_ROUTINE pThreadProc,
LPVOID pParam=NULL,
int iPriority=THREAD_PRIORITY_NORMAL)
{
_ASSERTE(m_hThread==NULL);
_ASSERTE(pThreadProc);
DWORD dwThreadID;
#if defined(_MT) || defined(_DLL)
m_hThread = (HANDLE) _beginthreadex(NULL, 0, (UINT (WINAPI*)(void*))pThreadProc, pParam, 0, (UINT*)&dwThreadID);
#else
m_hThread = ::CreateThread(NULL, 0, pThreadProc, pParam, 0, &dwThreadID);
#endif
if( m_hThread==NULL ) return FALSE;
if( iPriority!=THREAD_PRIORITY_NORMAL ) {
if( !::SetThreadPriority(m_hThread, iPriority) ) {
_ASSERTE(!"Couldn't set thread priority");
}
}
return TRUE;
}
BOOL Release()
{
if( m_hThread==NULL ) return TRUE;
if( ::CloseHandle(m_hThread)==FALSE ) return FALSE;
m_hThread = NULL;
return TRUE;
}
void Attach(HANDLE hThread)
{
_ASSERTE(m_hThread==NULL);
m_hThread = hThread;
}
HANDLE Detach()
{
HANDLE hThread = m_hThread;
m_hThread = NULL;
return hThread;
}
BOOL SetPriority(int iPriority) const
{
_ASSERTE(m_hThread);
return ::SetThreadPriority(m_hThread, iPriority);
}
int GetPriority() const
{
_ASSERTE(m_hThread);
return ::GetThreadPriority(m_hThread);
}
BOOL Suspend()
{
_ASSERTE(m_hThread);
if( m_bSuspended ) return TRUE;
if( ::SuspendThread(m_hThread)==-1 ) return FALSE;
m_bSuspended = true;
return TRUE;
}
BOOL Resume()
{
_ASSERTE(m_hThread);
if( !m_bSuspended ) return TRUE;
if( ::ResumeThread(m_hThread)==-1 ) return FALSE;
m_bSuspended = false;
return TRUE;
}
BOOL IsSuspended() const
{
_ASSERTE(m_hThread);
return m_bSuspended==true;
}
BOOL IsRunning() const
{
if( m_hThread==NULL ) return FALSE;
DWORD dwCode = 0;
::GetExitCodeThread(m_hThread, &dwCode);
return dwCode==STILL_ACTIVE;
}
BOOL WaitForThread(DWORD dwTimeout=INFINITE) const
{
_ASSERTE(m_hThread);
return ::WaitForSingleObject(m_hThread, dwTimeout);
}
BOOL Terminate(DWORD dwExitCode = 0) const
{
// See Q254956 why calling this could be a bad idea!
_ASSERTE(m_hThread);
return ::TerminateThread(m_hThread, dwExitCode);
}
BOOL GetExitCode(DWORD* pExitCode) const
{
_ASSERTE(m_hThread);
_ASSERTE(pExitCode);
return ::GetExitCodeThread(m_hThread, pExitCode);
}
#if(WINVER >= 0x0500)
BOOL GetThreadTimes(LPFILETIME lpCreationTime, LPFILETIME lpExitTime, LPFILETIME lpKernelTime, LPFILETIME lpUserTime) const
{
_ASSERTE(m_hThread);
_ASSERTE(lpExitTime!=NULL && lpKernelTime!=NULL && lpUserTime!=NULL);
return ::GetThreadTimes(m_hThread, lpExitTime, lpKernelTime, lpUserTime);
}
#endif
operator HANDLE() const { return m_hThread; }
};

typedef CThreadT<false> CThreadHandle;
typedef CThreadT<true> CThread;


/////////////////////////////////////////////////////////////////////////////
// CThreadImpl

class CThreadImpl : public CThread
{
public:
bool volatile m_bStopped; // Signals when thread should stop (thread loop should exit)

CThreadImpl() : m_bStopped(false)
{
}
virtual ~CThreadImpl()
{
// NOTE: Remember destructors cannot call overrides!
Stop();
}
virtual BOOL Start()
{
m_bStopped = false;
if( !Create(ThreadProc, (LPVOID)this) ) return FALSE;
return TRUE;
}
virtual void Stop()
{
if( SignalStop()==FALSE ) return;
WaitForThread();
Release();
}
BOOL SignalStop()
{
if( m_hThread==NULL ) return FALSE;
m_bStopped = true;
if( m_bSuspended ) Resume();
return TRUE;
}
BOOL ShouldStop() const
{
_ASSERTE(m_hThread);
return m_bStopped==true;
}

static DWORD WINAPI ThreadProc(LPVOID pData)
{
CThreadImpl* pThis = static_cast<cthreadimpl*>(pData);
#if defined(_MT) || defined(_DLL)
ATLTRY(_endthreadex( pThis->Run() ));
return 0;
#else
DWORD dwRet = 0;
ATLTRY(dwRet = pThis->Run());
return dwRet;
#endif
}
virtual DWORD Run()
{
_ASSERTE(false); // must override this
/*
// Sample thread code...
while( !ShouldStop() ) {
...
}
*/
return 0;
}
};


/////////////////////////////////////////////////////////////////////////////
// CEvent

class CEvent
{
public:
HANDLE m_hEvent;
CEvent() : m_hEvent(INVALID_HANDLE_VALUE)
{
}
~CEvent()
{
Close();
}
BOOL Create(LPCTSTR pstrName, BOOL bManualReset=FALSE, BOOL bInitialState=FALSE, LPSECURITY_ATTRIBUTES pEventAttributes=NULL)
{
_ASSERTE(!::IsBadStringPtr(pstrName,-1));
_ASSERTE(m_hEvent==INVALID_HANDLE_VALUE);
m_hEvent = ::CreateEvent(pEventAttributes, bManualReset, bInitialState, pstrName);
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return m_hEvent!=INVALID_HANDLE_VALUE;
}
BOOL Open(LPCTSTR pstrName, DWORD dwDesiredAccess=EVENT_ALL_ACCESS, BOOL bInheritHandle=TRUE)
{
_ASSERTE(!::IsBadStringPtr(pstrName,-1));
_ASSERTE(m_hEvent==INVALID_HANDLE_VALUE);
m_hEvent = ::OpenEvent(dwDesiredAccess, bInheritHandle, pstrName);
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return m_hEvent!=INVALID_HANDLE_VALUE;
}
BOOL IsOpen() const
{
return m_hEvent != INVALID_HANDLE_VALUE;
}
void Close()
{
if( m_hEvent==INVALID_HANDLE_VALUE ) return;
::CloseHandle(m_hEvent);
m_hEvent = INVALID_HANDLE_VALUE;
}
BOOL ResetEvent()
{
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return ::ResetEvent(m_hEvent);
}
BOOL SetEvent()
{
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return ::SetEvent(m_hEvent);
}
BOOL PulseEvent()
{
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return ::PulseEvent(m_hEvent);
}
BOOL WaitForEvent(DWORD dwTimeout=INFINITE)
{
_ASSERTE(m_hEvent!=INVALID_HANDLE_VALUE);
return ::WaitForSingleObject(m_hEvent, dwTimeout)==WAIT_OBJECT_0;
}
operator HANDLE() const { return m_hEvent; }
};


#endif // !defined(AFX_THREAD_H__20000927_7992_09B2_A0EF_0080AD509054__INCLUDED_)
GeneralRe: CThread by Bjarke Viksoe Pin
Amer Gerzic21-Apr-03 2:05
Amer Gerzic21-Apr-03 2:05 
GeneralATL Thread Classes Pin
mdgray19-Apr-03 18:28
mdgray19-Apr-03 18:28 
GeneralRe: ATL Thread Classes Pin
Amer Gerzic20-Apr-03 7:25
Amer Gerzic20-Apr-03 7:25 
GeneralRe: ATL Thread Classes Pin
mdgray20-Apr-03 13:59
mdgray20-Apr-03 13:59 
GeneralNice! Pin
Daniel Lohmann19-Apr-03 13:36
Daniel Lohmann19-Apr-03 13:36 
GeneralRe: Nice! Pin
Amer Gerzic20-Apr-03 5:42
Amer Gerzic20-Apr-03 5:42 
GeneralRe: Nice! Pin
Daniel Lohmann20-Apr-03 10:54
Daniel Lohmann20-Apr-03 10:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.