Introduction
The WorkerClass is an extremely simple MFC class to help you with multi-threading. Any object you want to do work that needs to be in its own thread derives from 'WorkerClass'. Implement the DoWork
function to carry out the long process - and then call Start
/Stop
/Pause
on the object to carry out the work.
Because of C++'s ability to handle multiple inheritence this means you can make a CDialog a worker class, and any CObject
a worker class - in fact, any class at all can be a WorkerClass.
Why?
The DoWork
function is called in a seperate thread, but it is a member of the class. This means your threaded function can access all of the classes member variables and functions and so on. A threaded process function generally has to be called as a global or static function - meaning this sort of data has to be passed to it. See what I mean in the example below.
The Example Application
I create a dialog based MFC application - and change the dialog definition as such:
#pragma once
#include "WorkerClass.h"
#include "afxwin.h"
Important change number one - include the WorkerClass header! Don't forget to add the source file to the project too!
class CWorkerClassExampleDlg : public CDialog,
public WorkerClass {
public:
CWorkerClassExampleDlg(CWnd* pParent = NULL);
Important change number two - the dialog inherits from CDialog AND WorkerClass! This is crucial! Why no hungarian notation for WorkerClass (i.e. CWorkerClass?). Well the class is actually not reliant on MFC. I generally only use the 'C' prefix on MFC classes. WorkerClass will work fine in plain Win32 applications. The only other important addition is below:
virtual void DoWork();
This function must be overriden in the CDialog. It's the function that does the threaded work.
The only other change we need to make at this stage is to add the implementation:
void CWorkerClassExampleDlg::DoWork()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
while(GetStatus() != Stopping)
{
Sleep(100);
}
}
Typically the AFX_MANAGE_STATE
macro isn't needed. However, it's safer to put it in, it attempts to make Window and Control handles share across threads a little bit better.
Just carry out the long operation in this function - that's all there is too it! One thing you can also do is regularly check to see if the WorkerClass has been asked to stop, by calling:
if(GetStatus() == Stopping)
When you call 'Stop' on the Worker Class the status is set to 'Stopping'. This is politely asking the work to stop if possible. In the example above we just keep looping until we're told to stop.
You don't NEED to do this - and you can forcibly terminate the work by calling 'Terminate', but it's slightly more graceful.
Controlling Work
Let's say I have a 'Start Work' button on the dialog - here's what I can do:
void CWorkerClassExampleDlg::OnBnClickedButtonStart()
{
Start();
easy. How do I stop the thread? Call Stop()
. Toggle pause on or off by calling Pause()
.
Members
Here's the functions you need to know about!
Start()
Start the WorkerClass.
Stop()
Ask the WorkerClass to stop. This function will not return until the thread has genuinely stopped - so depending on the work and how well the function has been written this could possibly take some time.
Terminate()
Force the WorkerClass to stop. This function kills the thread and returns immediately.
Pause()
Pauses the execution of the thread if working, and unpauses it if it's not working.
Status GetStatus() const
Get's the current status of the Worker Class:
Stopped - The worker class isn't working. You can start it.
Stopping - The worker class has been asked to stop.
Paused - The worker class has been suspended - but it can be unpaused or terminated.
Working - The worker class is working. You can stop, terminate or pause it.
And The Rest
Leave comments if you have any questions or problems! This class isn't heavyweight - but it's perfect for quickly multithreading work. Enjoy!
History
- 14th January 2009 - Wrote the first revision of the article.