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
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.
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:
#include <span class="code-string">"WorkerClass.h"</span>
#include <span class="code-string">"afxwin.h"</span>
Important change number one - include the WorkerClass header! Don't forget to add the source file to the project too!
class CWorkerClassExampleDlg : public CDialog,
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:
while(GetStatus() != Stopping)
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.
Let's say I have a 'Start Work' button on the dialog - here's what I can do:
easy. How do I stop the thread? Call
Stop(). Toggle pause on or off by calling
Here's the functions you need to know about!
Start the WorkerClass.
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.
Force the WorkerClass to stop. This function kills the thread and returns immediately.
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!
- 14th January 2009 - Wrote the first revision of the article.