It's not volatile, this is criticalFor far too long I have listened to the likes of those who believe that the volatile modifier is the end-all answer to multi-threaded development.
Parallelism is complex! Given its complexity, it should not be "screwed with" by any person not understanding the difference between "mutual-exclusive synchronization" and "volatile variables" (CPU cache optimization exclusion).
Take the example below, read it, study it, compile it and run it. Once you have satisfied yourself that it will always print the number 5,000,000 to the console as it's only possible output, I would then like you to remove the [EnterCriticalSection] / [LeaveCriticalSection] pair and give the application another run - amazed yet?
How can this be, I mean I'm using the volatile modifier?
(please note the knee deep puddle of sarcasm)
//-------------------------------------------------------------------------------
#include "windows.h"
#include "stdio.h"
//-------------------------------------------------------------------------------
volatile unsigned int iGlobalCounter = 0;
CRITICAL_SECTION CS;
//-------------------------------------------------------------------------------
DWORD WINAPI threadProc(LPVOID pData)
{
HANDLE hEvent = (HANDLE) pData; //The synchronization object handle.
//Increment [iGlobalCounter] 1 million times.
for(unsigned int i = 0; i < 1000000; i++)
{
//Comment out [EnterCriticalSection] and [LeaveCriticalSection],
// which control access to iGlobalCounter to see this
// multi-threaded example go awry.
EnterCriticalSection(&CS);
iGlobalCounter++;
LeaveCriticalSection(&CS);
}
//Set the synchronization object to let the calling
// thread know that this workload is complete.
SetEvent(hEvent);
return 0;
}
//-------------------------------------------------------------------------------
void IncrementGlobalCounter(void)
{
int iThreadCount = 5; //Create 5 threads.
char sEventName[255];
HANDLE *hEvent = NULL;
//Allocate enough RAM to hold "wait event" handles for each thread.
if(!(hEvent = (HANDLE *) calloc(sizeof(HANDLE), iThreadCount)))
{
printf("Memory allocation error.\n");
return;
}
//Initialize a critical section object.
memset(&CS, 0, sizeof(CS));
InitializeCriticalSection(&CS);
for(int iThread = 0; iThread < iThreadCount; iThread++)
{
//Create a unique event name for a synchronization object.
sprintf(sEventName, "Thread_Event_%d_%d", GetTickCount(), iThread);
//Create a synchronization object.
hEvent[iThread] = CreateEvent(NULL, FALSE, FALSE, sEventName);
//Create a thread, passing it the handle to the synchronization object.
CreateThread(NULL, 0, threadProc, hEvent[iThread], 0, NULL);
}
//Wait on all threads to complete.
WaitForMultipleObjects(iThreadCount, hEvent, TRUE, INFINITE);
//Clean up the allocated RAM and the critical section object.
free(hEvent);
DeleteCriticalSection(&CS);
}
//-------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
//CPU Count and CPU Affinity validation
SYSTEM_INFO SI;
memset(&SI, 0, sizeof(SI));
GetSystemInfo(&SI);
if(SI.dwNumberOfProcessors < 0)
{
printf("Your must have more than one CPU core for this example.\n");
return 1;
}
//Lets perform the whole test 10 times.
for(int iTestNumber = 0; iTestNumber < 10; iTestNumber++)
{
iGlobalCounter = 0; //Reset the counter to zero.
IncrementGlobalCounter(); //Increment the global counter.
//Output the totaled global counter. It should
// be 5,000,000 ([5 threads] * [1 million increments]).
printf("iGlobalCounter: %d\n", iGlobalCounter);
}
system("Pause");
}
//-------------------------------------------------------------------------------
Is this code snippet, product or advice warrantied against ill-effect and/or technical malaise? No. No it's not! Not expressed - Not implied - not at all.