Guide to Choose and Implement a Worker Thread
_Flaviu - 30/Jul/2020
_Flaviu - 30/Jul/2020
[SHOWTOGROUPS=4,20,22]
A guide to choosing and implementing a worker thread in your application
This article intends to help programmers to choose and implement a worker thread in a Win32 / MFC application.
Introduction
First, there are several thread functions: _beginthread(), CreateThread, AfxBeginThread. What should I choose? To decide, we have to enumerate them.
Using the Code
Hide Copy Code
class CSample
{
public:
CSample() { m_hThread = 0; }
~CSample() { CloseHandle(m_hThread); }
bool Create()
{
m_hThread = CreateThread(0, 0, ThreadFunction, this, 0, 0);
if(! m_hThread)
{
return false; // Could not create thread
}
return true;
}
private:
HANDLE m_hThread;
private:
static DWORD WINAPI ThreadFunction(LPVOID pvParam);
};
and thread function:
Hide Copy Code
DWORD CSample::ThreadFunction(LPVOID pvParam)
{
// do some useful things
}
Using:
Hide Copy Code
CSample sample;
if (! Create())
AfxMessageBox(_T("Could not create thread !"));
If your project is a MFC project, you better use Для просмотра ссылки Войдиили Зарегистрируйся which is part of MFC.
Before you setup a thread method, you will have to take care of two things:
// CMyDialog header
#define WMU_NOTIFYTHREAD (WM_APP + 12) // notification message
// from thread
#define THREADUPDATESTARTED 1
#define THREADUPDATESTOPPED 2
#define THREADUPDATEINFO 3
class CMyDialog : public CDialog
{
.....
protected:
static UINT WorkerThread(LPVOID lpParam); // this is the static method used
// inside of child thread
protected:
BOOL m_bThreadActive; // the variable is used to know
// the state of the child thread
volatile BOOL m_bRunThread; // the variable is used to control
// the child thread from outside
CWinThread* m_pWinThread; // CWinThread object used with AfxBeginThread
.....
// Generated message map functions
protected:
//{{AFX_MSG(CMyDialog)
afx_msg void OnDestroy();
afx_msg LRESULT OnNotifyThread(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
};
The implementation looks like this:
Hide Shrink
Copy Code
// CMyDialog implementation
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
//{{AFX_MSG_MAP(CMyDialog)
ON_WM_DESTROY()
//}}AFX_MSG_MAP
ON_MESSAGE(WMU_NOTIFYTHREADUPDATE, &CMyDialog::OnNotifyThreadUpdate)
END_MESSAGE_MAP()
CMyDialog::CMyDialog()
:m_pWinThread(NULL) // initialize all variables
, m_bRunThread(FALSE)
, m_bThreadActive(FALSE)
{
//
}
CMyDialog::StartThread()
{
// create thread as suspended
m_pWinThread = AfxBeginThread(&CMyDialog::WorkerThread, (LPVOID)this,
THREAD_PRIORITY_BELOW_NORMAL, CREATE_SUSPENDED, 0, NULL);
// setup additional information
m_pWinThread->m_bAutoDelete = TRUE; // this will tell the thread
// to auto clean at thread exit
m_bRunThread = TRUE;
m_pWinThread->ResumeThread(); // start the thread
}
CMyDialog::StopThread()
{
m_bRunThread = FALSE; // setup this flag as FALSE will stop the
// looping inside of thread and so the thread
}
and as WorkerThread method, you can have a kind of:
Hide Copy Code
UINT CMyDialog::WorkerThread(LPVOID lpParam)
{
CMyDialog* pDlg = (CMyDialog*)lpParam;
if (NULL == pDlg || NULL == pDlg->GetSafeHwnd())
return 1; // fail thread
:ostMessage(pDlg->GetSafeHwnd(),
WMU_NOTIFYTHREAD, THREADUPDATESTARTED, 0); // notify main thread that child thread
// is started
BOOL bJobDone = FALSE;
while (pDlg->m_bRunThread || ! bJobDone) // Run loop inside thread
{
// do actual work
// if the job has been done, then set up bJobDone as TRUE
}
:ostMessage(pDlg->GetSafeHwnd(),
WMU_NOTIFYTHREAD, THREADUPDATESTOPPED, 0); // notify main thread that child thread
// is stopped
return 0;
}
Also, we have to receive the notification from child thread:
Hide Copy Code
LRESULT CMyDialog::OnNotifyThread(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case THREADUPDATESTARTED:
m_bThreadActive = TRUE;
break;
case THREADUPDATESTOPPED:
m_bRunThread = FALSE;
m_bThreadActive = FALSE;
break;
}
return 1;
}
You also have to take care not to close your app(dialog) until you are sure that child thread is stopped:
Hide Copy Code
void CMyDialog::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
if (NULL != m_pWinThread) // if thread is still active
{
m_bRunThread = FALSE; // close the looping inside thread
switch (WaitForSingleObject(m_pWinThread->m_hThread, 3000)) // wait for thread
// to close for 3 seconds
{
case WAIT_OBJECT_0:
TRACE(_T("The worker thread just finished\n"));
break;
case WAIT_TIMEOUT:
TRACE(_T("Cannot stop gracefully the worker thread\n"));
break;
}
}
}
There is another situation inside thread method: you can have there a waitable function. Here is a sample:
Hide Shrink
Copy Code
UINT AFX_CDECL CMyDialog::WorkerThread(LPVOID lpParam)
{
CMyDialog* pDlg = (CMyDialog*)lpParam;
if (NULL == pDlg || NULL == pDlg->GetSafeHwnd())
return 1; // fail thread
:ostMessage(pDlg->GetSafeHwnd(), WMU_NOTIFYTHREAD, THREADUPDATESTARTED, 0); // notify
// main thread that child thread is started
CArray<HANDLE, HANDLE&> arrHandle;
arrHandle.Add(pDlg->m_EventChange.m_hObject);
arrHandle.Add(pDlg->m_EventStop.m_hObject);
BOOL bContinue = TRUE;
while (bContinue)
{
const DWORD dwResult = WaitForMultipleObjects(arrHandle.GetSize(),
arrHandle.GetData(), FALSE, INFINITE);
switch (dwResult)
{
case WAIT_OBJECT_0: // arrHandle elem 0 (pDlg->m_EventChange.m_hObject)
// do something useful here
break;
case WAIT_OBJECT_0 + 1: // arrHandle elem 1 (pDlg->m_EventStop.m_hObject)
bContinue = FALSE;
break;
}
}
:ostMessage(pDlg->GetSafeHwnd(),
WMU_NOTIFYTHREAD, THREADUPDATESTOPPED, 0); // notify main thread that
// child thread is stopped
return 0;
}
[/SHOWTOGROUPS]
A guide to choosing and implementing a worker thread in your application
This article intends to help programmers to choose and implement a worker thread in a Win32 / MFC application.
Introduction
First, there are several thread functions: _beginthread(), CreateThread, AfxBeginThread. What should I choose? To decide, we have to enumerate them.
Using the Code
- _beginthread - Besides the sister _beginthreadex function, is part of C run - time library
Для просмотра ссылки Войдиили Зарегистрируйся, you can find a small example of how to use them in a real situation. - CreateThread - Для просмотра ссылки Войди
или Зарегистрируйся.
Hide Copy Code
class CSample
{
public:
CSample() { m_hThread = 0; }
~CSample() { CloseHandle(m_hThread); }
bool Create()
{
m_hThread = CreateThread(0, 0, ThreadFunction, this, 0, 0);
if(! m_hThread)
{
return false; // Could not create thread
}
return true;
}
private:
HANDLE m_hThread;
private:
static DWORD WINAPI ThreadFunction(LPVOID pvParam);
};
and thread function:
Hide Copy Code
DWORD CSample::ThreadFunction(LPVOID pvParam)
{
// do some useful things
}
Using:
Hide Copy Code
CSample sample;
if (! Create())
AfxMessageBox(_T("Could not create thread !"));
If your project is a MFC project, you better use Для просмотра ссылки Войди
Before you setup a thread method, you will have to take care of two things:
- a way to control the thread method from outside of your child thread
- a way to know the main thread what is the state of your child thread
// CMyDialog header
#define WMU_NOTIFYTHREAD (WM_APP + 12) // notification message
// from thread
#define THREADUPDATESTARTED 1
#define THREADUPDATESTOPPED 2
#define THREADUPDATEINFO 3
class CMyDialog : public CDialog
{
.....
protected:
static UINT WorkerThread(LPVOID lpParam); // this is the static method used
// inside of child thread
protected:
BOOL m_bThreadActive; // the variable is used to know
// the state of the child thread
volatile BOOL m_bRunThread; // the variable is used to control
// the child thread from outside
CWinThread* m_pWinThread; // CWinThread object used with AfxBeginThread
.....
// Generated message map functions
protected:
//{{AFX_MSG(CMyDialog)
afx_msg void OnDestroy();
afx_msg LRESULT OnNotifyThread(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
};
The implementation looks like this:
Hide Shrink
// CMyDialog implementation
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
//{{AFX_MSG_MAP(CMyDialog)
ON_WM_DESTROY()
//}}AFX_MSG_MAP
ON_MESSAGE(WMU_NOTIFYTHREADUPDATE, &CMyDialog::OnNotifyThreadUpdate)
END_MESSAGE_MAP()
CMyDialog::CMyDialog()
:m_pWinThread(NULL) // initialize all variables
, m_bRunThread(FALSE)
, m_bThreadActive(FALSE)
{
//
}
CMyDialog::StartThread()
{
// create thread as suspended
m_pWinThread = AfxBeginThread(&CMyDialog::WorkerThread, (LPVOID)this,
THREAD_PRIORITY_BELOW_NORMAL, CREATE_SUSPENDED, 0, NULL);
// setup additional information
m_pWinThread->m_bAutoDelete = TRUE; // this will tell the thread
// to auto clean at thread exit
m_bRunThread = TRUE;
m_pWinThread->ResumeThread(); // start the thread
}
CMyDialog::StopThread()
{
m_bRunThread = FALSE; // setup this flag as FALSE will stop the
// looping inside of thread and so the thread
}
and as WorkerThread method, you can have a kind of:
Hide Copy Code
UINT CMyDialog::WorkerThread(LPVOID lpParam)
{
CMyDialog* pDlg = (CMyDialog*)lpParam;
if (NULL == pDlg || NULL == pDlg->GetSafeHwnd())
return 1; // fail thread
:ostMessage(pDlg->GetSafeHwnd(),
WMU_NOTIFYTHREAD, THREADUPDATESTARTED, 0); // notify main thread that child thread
// is started
BOOL bJobDone = FALSE;
while (pDlg->m_bRunThread || ! bJobDone) // Run loop inside thread
{
// do actual work
// if the job has been done, then set up bJobDone as TRUE
}
:ostMessage(pDlg->GetSafeHwnd(),
WMU_NOTIFYTHREAD, THREADUPDATESTOPPED, 0); // notify main thread that child thread
// is stopped
return 0;
}
Also, we have to receive the notification from child thread:
Hide Copy Code
LRESULT CMyDialog::OnNotifyThread(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case THREADUPDATESTARTED:
m_bThreadActive = TRUE;
break;
case THREADUPDATESTOPPED:
m_bRunThread = FALSE;
m_bThreadActive = FALSE;
break;
}
return 1;
}
You also have to take care not to close your app(dialog) until you are sure that child thread is stopped:
Hide Copy Code
void CMyDialog::OnDestroy()
{
CDialog::OnDestroy();
// TODO: Add your message handler code here
if (NULL != m_pWinThread) // if thread is still active
{
m_bRunThread = FALSE; // close the looping inside thread
switch (WaitForSingleObject(m_pWinThread->m_hThread, 3000)) // wait for thread
// to close for 3 seconds
{
case WAIT_OBJECT_0:
TRACE(_T("The worker thread just finished\n"));
break;
case WAIT_TIMEOUT:
TRACE(_T("Cannot stop gracefully the worker thread\n"));
break;
}
}
}
There is another situation inside thread method: you can have there a waitable function. Here is a sample:
Hide Shrink
UINT AFX_CDECL CMyDialog::WorkerThread(LPVOID lpParam)
{
CMyDialog* pDlg = (CMyDialog*)lpParam;
if (NULL == pDlg || NULL == pDlg->GetSafeHwnd())
return 1; // fail thread
:ostMessage(pDlg->GetSafeHwnd(), WMU_NOTIFYTHREAD, THREADUPDATESTARTED, 0); // notify
// main thread that child thread is started
CArray<HANDLE, HANDLE&> arrHandle;
arrHandle.Add(pDlg->m_EventChange.m_hObject);
arrHandle.Add(pDlg->m_EventStop.m_hObject);
BOOL bContinue = TRUE;
while (bContinue)
{
const DWORD dwResult = WaitForMultipleObjects(arrHandle.GetSize(),
arrHandle.GetData(), FALSE, INFINITE);
switch (dwResult)
{
case WAIT_OBJECT_0: // arrHandle elem 0 (pDlg->m_EventChange.m_hObject)
// do something useful here
break;
case WAIT_OBJECT_0 + 1: // arrHandle elem 1 (pDlg->m_EventStop.m_hObject)
bContinue = FALSE;
break;
}
}
:ostMessage(pDlg->GetSafeHwnd(),
WMU_NOTIFYTHREAD, THREADUPDATESTOPPED, 0); // notify main thread that
// child thread is stopped
return 0;
}
[/SHOWTOGROUPS]