Codwiz51's Wiki

RSS

Navigation







Quick Search
»
Advanced Search »
Next Article in Series: ATL Threading, Part 2

This is part 1 of some work I have been meaning to write up concerning threading and ATL. This article covers CWorkerThread, IWorkerThreadClient, simple waitable events and mutexes.

Visual Studio 2008 Solution Download: Worker Thread Example

ATL, CWorkerThread, IWorkerThreadClient, Events and Mutexes

I was recently working with an ATL component that needed to perform some device reading functionality in a background thread.

The MSDN documentation regarding ATL's CWorkerThread implementation is pretty good, but the examples I found in MSDN and via internet searches either didn't illustrate the object, or were overly complex.

The application illustrates how to use ATL's CWorkerThread, how to use event handles to signal threads and how to use a mutex to synchronize data access between the worker thread and the application thread.

// WorkerThreadExample.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

HANDLE hBlockingMutex;

// The worker thread class must implement the IWorkerThreadClient interface
class CWorkerClient: public IWorkerThreadClient
{
public:
	// Constructor, which does nothing
	CWorkerClient() {};
	HRESULT Execute(DWORD_PTR dwParam, HANDLE hObject);
	HRESULT CloseHandle(HANDLE hHandle);
};

// Execute is called repeatedly by CWorkerhread
HRESULT CWorkerClient::Execute(DWORD_PTR dwParam, HANDLE hObject)
{
	// Obtain ownership of the mutex.  This allows the
	// program to safely access the integer pointer
	// passed in dwParam
	WaitForSingleObject(hBlockingMutex, INFINITE);

	// The following complex looking code casts
	// dwParam to an integer pointer, dereferences
	// the pointer and increments the value by 1
	int t = (*((int *)dwParam))++;

	// I copied the integer to a local variable so the
	// mutex could be release quickly
	ReleaseMutex(hBlockingMutex);

	// This will have to suffice for an imitation of real work
	::printf("Child = %d, Param = %d\n", ::GetCurrentThreadId(), t);
	return S_OK;
}

// Closehandle shuts down the thread
// This method is called by RemoveHandle (see _tmain)
HRESULT CWorkerClient::CloseHandle(HANDLE hHandle)
{
	::CloseHandle(hHandle);
	return S_OK;
}

int _tmain(int argc, _TCHAR* argv[])
{
	// Declare thread instance
	CWorkerThread<CRTThreadTraits> cWth;

	// Declare worker client instance
	CWorkerClient cWcl;

	// Waitable handle to event
	HANDLE hEvent;

	// A pointer to count by
	// We can pass any sort of 32 bit pointer in
	// dwParam.  In an actual application, we
	// would probably pass a pointer to a struct
	// or even the 'this' pointer.
	int* pInt = new int();
	*pInt = 0;
	DWORD_PTR dwParam = (DWORD_PTR)pInt;
	
	// Initialize the thread
	cWth.Initialize();

	// Create a waitable event
	hEvent = ::CreateEvent(NULL, TRUE, FALSE, _T("MyEvent"));

	// Create a waitable mutex
	hBlockingMutex = ::CreateMutex(NULL, FALSE, _T("MyMutex"));

	// Add the handle to the worker thread instance
	cWth.AddHandle(hEvent, &cWcl, dwParam);

	::printf("Parent = %d\n", ::GetCurrentThreadId());
	::printf("Hit [ENTER] to signal the object ");
	::fflush(stdout);
	getchar();

	// Signal the event handle, which launches the thread
	::SetEvent(hEvent);

	// Loop in the main thread, printing the value from
	// the dereferenced pointer
	do
	{
		WaitForSingleObject(hBlockingMutex, INFINITE);
		int t = *pInt;
		ReleaseMutex(hBlockingMutex);
		::printf("Parent = %d, int = %d\n", ::GetCurrentThreadId(), t);
		// This is a bit of fun.  We test to see if the thread has been
                // executed at least 90 times.  Remember, we are sleeping, so
		// t may exceed 90 by a considerable number of tics.
		if (t > 90)
		{
			// Interesting to see that our thread runs a couple 
			// of more times after the event is reset
			// Apparently, there is some latency
			::printf("Reset event\n");
			::ResetEvent(hEvent);
			::printf("Remove handle\n");
			cWth.RemoveHandle(hEvent);
			::printf("Thread shutdown\n");
			::fflush(stdout);
			cWth.Shutdown();
			break;
		}
		else
			// if t is less than 90, sleep for 10 ms
			::Sleep(10);

	} while (true);
	
	return 0;
}

Here's the program output:
Parent = 2492
Hit [ENTER] to signal the object
Parent = 2492, int = 0
Child = 2708, Param = 0
Child = 2708, Param = 1
Child = 2708, Param = 2
Child = 2708, Param = 3
Child = 2708, Param = 4
Child = 2708, Param = 5
Child = 2708, Param = 6
Child = 2708, Param = 7
Child = 2708, Param = 8
Child = 2708, Param = 9
Child = 2708, Param = 10
Child = 2708, Param = 11
Child = 2708, Param = 12
Child = 2708, Param = 13
Child = 2708, Param = 14
Child = 2708, Param = 15
Child = 2708, Param = 16
Child = 2708, Param = 17
Child = 2708, Param = 18
Child = 2708, Param = 19
Child = 2708, Param = 20
Child = 2708, Param = 21
Child = 2708, Param = 22
Child = 2708, Param = 23
Child = 2708, Param = 24
Child = 2708, Param = 25
Child = 2708, Param = 26
Child = 2708, Param = 27
Child = 2708, Param = 28
Child = 2708, Param = 29
Child = 2708, Param = 30
Child = 2708, Param = 31
Child = 2708, Param = 32
Child = 2708, Param = 33
Child = 2708, Param = 34
Child = 2708, Param = 35
Child = 2708, Param = 36
Parent = 2492, int = 37
Child = 2708, Param = 37
Child = 2708, Param = 38
Child = 2708, Param = 39
Child = 2708, Param = 40
Child = 2708, Param = 41
Child = 2708, Param = 42
Child = 2708, Param = 43
Child = 2708, Param = 44
Child = 2708, Param = 45
Child = 2708, Param = 46
Child = 2708, Param = 47
Child = 2708, Param = 48
Child = 2708, Param = 49
Child = 2708, Param = 50
Child = 2708, Param = 51
Child = 2708, Param = 52
Child = 2708, Param = 53
Child = 2708, Param = 54
Child = 2708, Param = 55
Child = 2708, Param = 56
Child = 2708, Param = 57
Child = 2708, Param = 58
Child = 2708, Param = 59
Child = 2708, Param = 60
Child = 2708, Param = 61
Child = 2708, Param = 62
Child = 2708, Param = 63
Child = 2708, Param = 64
Child = 2708, Param = 65
Parent = 2492, int = 66
Child = 2708, Param = 66
Child = 2708, Param = 67
Child = 2708, Param = 68
Child = 2708, Param = 69
Child = 2708, Param = 70
Child = 2708, Param = 71
Child = 2708, Param = 72
Child = 2708, Param = 73
Child = 2708, Param = 74
Child = 2708, Param = 75
Child = 2708, Param = 76
Child = 2708, Param = 77
Child = 2708, Param = 78
Child = 2708, Param = 79
Child = 2708, Param = 80
Child = 2708, Param = 81
Child = 2708, Param = 82
Child = 2708, Param = 83
Child = 2708, Param = 84
Child = 2708, Param = 85
Child = 2708, Param = 86
Child = 2708, Param = 87
Child = 2708, Param = 88
Child = 2708, Param = 89
Child = 2708, Param = 90
Child = 2708, Param = 91
Child = 2708, Param = 92
Child = 2708, Param = 93
Child = 2708, Param = 94
Child = 2708, Param = 95
Parent = 2492, int = 96
Child = 2708, Param = 96
Reset event
Child = 2708, Param = 97
Remove handle
Thread shutdown

Visual Studio 2008 Solution Download: Worker Thread Example

Next Article in Series: ATL Threading, Part 2
  Name Size
- WorkerThreadExample.7z 4.08 KB

ScrewTurn Wiki version 3.0.2.509. Some of the icons created by FamFamFam.