Click here to Skip to main content
Click here to Skip to main content

Tagged as

Worker Class

, 16 Jan 2009 GPL3
Rate this:
Please Sign up or sign in to vote.
An article showing how a WokerClass object can simplify multi-threaded operations.

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:

// WorkerClassExampleDlg.h : header file
//

#pragma once

#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!

// CWorkerClassExampleDlg dialog
class CWorkerClassExampleDlg : public CDialog, 
public WorkerClass	//	This makes the dialog a worker class.
{
// Construction
public:
	CWorkerClassExampleDlg(CWnd* pParent = NULL);	// standard constructor

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:

	//	The most important part of the worker class - the DoWork function.
	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());

	//	As long as we're not being told to stop, we can do some pretend work!
	while(GetStatus() != Stopping)
	{
		//	Blah blah blah - do some long winded work!

		//	Wait.
		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 the thread.
	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.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author


Comments and Discussions

 
GeneralMy vote of 1 PinmemberPanic2k321-Aug-10 16:28 
GeneralSome Criticism: Beginners Please Read This PinmemberMike O'Neill22-Jan-09 6:34 
I think it would be better if beginners did not use this class, and did not try to model their own code after anything in the article. Here's why:
 
1. Use of "dangerous" function: TerminateThread(): Even the MSDN notes for this function state explicitly that "TerminateThread is a dangerous function that should only be used in the most extreme cases". All experts on multithreading scorn its use, for the reasons given in MSDN, and the code here does not even try to ameliorate any of the problems that its use can cause.
 
2. Use of functions that are reserved for use only for debuggers: SuspendThread() and ResumeThread(): The code is using these functions for thread synchronization (pausing, resuming etc). Again, MSDN is explicit. These functions are "primarily designed for use by debuggers. [They are] not intended to be used for thread synchronization."
 
3. Polling for an event: The code is using the Sleep() function (which itself is a sign of trouble) to poll for events (shutdown, startup etc). Polling is generally regarded as evil. As othere here have advised, use an Event for thread synchronization. That's what Events are designed for.
 
4. Cross-thread use of SendMessage: Generally, it's bad to send messages from one thread to another using SendMessage. SendMessage causes a thread to stop cold, until there is a response from the thread that owns the window targetted by the message. That's bad because it forces synchronization on threads that usually should be asynchronous of each other, and because it stops a thread needlessly while it waits for a response. Use PostMessage instead. Incidentally, this one is subtle. SendMessage is not found explicitly in the code. It's hidden inside the call to SetWindowText, which does a SendMessage of WM_SETTEXT.
 
5. Altering the GUI directly from a worker thread: A worker thread should never touch the UI directly. It should do so only indirectly, such as by posting messages to the GUI thread, asking the GUI thread to do so.
 
The list could go on for some time. I strongly recommend others not to use this code, and not to try and take any lessons from it.
GeneralThe design of WorkerClass class contradict MSDN! [modified] PinmemberVictor Nijegorodov22-Jan-09 4:54 
QuestionBusy waiting???? Pinmemberichramm21-Jan-09 2:07 
GeneralMy vote of 1 Pinmemberwa19-Jan-09 0:54 
GeneralRe: My vote of 1 PinmemberDave Kerr19-Jan-09 7:03 
GeneralKeep GUI alive PinmemberDavide Zaccanti19-Jan-09 0:22 
GeneralRe: Keep GUI alive PinmemberDave Kerr19-Jan-09 6:54 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411023.1 | Last Updated 16 Jan 2009
Article Copyright 2009 by Dave Kerr
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid