It seems that this question gets asked frequently, but I am not coming to any definitive conclusion. I need a little help on determining whether or not I should (or must!) implement locking code when accessing/modifying global variables when I have:
- global variables defined at file scope
- a single “worker” thread reading/writing to global variables
- calls from the main process thread calling accessor functions which return these globals
So the question is, should I be locking access to my global variables with mutexes?
More specifically, I am writing a C++ library which uses a webcam to track objects on a page of paper — computer vision is CPU intensive, so performance is critical. I have a single worker thread which is spun off in an Open() function. This thread handles all of the object tracking. It is terminated (indirectly w/global flag) when a Close() function is called.
It feels like I’m just asking for memory corruption, but I have observed no deadlock issues nor have I experienced any bad values returned from these accessor functions. And after several hours of research, the general impression I get is, “Meh, probably. Whatever. Have fun with that.” If I indeed should be using mutexes, why I have not experienced any problems yet?
Here is an over-simplification on my current program:
// *********** lib.h ***********
// Structure definitions
struct Pointer
{
int x, y;
};
// more...
// API functions
Pointer GetPointer();
void Start();
void Stop();
// more...
The implementation looks like this…
// *********** lib.cpp ***********
// Globals
Pointer p1;
bool isRunning = false;
HANDLE hWorkerThread;
// more...
// API functions
Pointer GetPointer()
{
// NOTE: my current implementation is actually returning a pointer to the
// global object in memory, not a copy of it, like below...
// Return copy of pointer data
return p1;
}
// more "getters"...
void Open()
{
// Create worker thread -- continues until Close() is called by API user
hWorkerThread = CreateThread(NULL, 0, DoWork, NULL, 0, NULL);
}
void Close()
{
isRunning = false;
// Wait for the thread to close nicely or else you WILL get nasty
// deadlock issues on close
WaitForSingleObject(hWorkerThread, INFINITE);
}
DWORD WINAPI DoWork(LPVOID lpParam)
{
while (isRunning)
{
// do work, including updating 'p1' about 10 times per sec
}
return 0;
}
Finally, this code is being called from an external executable. Something like this (pseudocode):
// *********** main.cpp ***********
int main()
{
Open();
while ( <esc not pressed> )
{
Pointer p = GetPointer();
<wait 50ms or so>
}
Close();
}
Is there perhaps a different approach I should be taking? This non-issue issue is driving me nuts today :-/ I need to ensure this library is stable and returning accurate values. Any insight would be greatly appreciated.
Thanks
I suppose it depends on what you are doing in your DoWork() function. Let’s assume it writes a point value to p1. At the very least you have the following possibility of a race condition that will return invalid results to the main thread:
Suppose the worker thread wants to update the value of p1. For example, lets change the value of p1 from (A, B) to (C, D). This will involve at least two operations, store C in x and store D in y. If the main thread decides to read the value of p1 in the GetPointer() function, it must perform at least two operations also, load value for x and load value for y. If the sequence of operations is:
The main thread will get the point (C, B), which is not correct.
This particular problem is not a good use of threads, since the main thread isn’t doing any real work. I would use a single thread, and an API like WaitForMultipleObjectsEx which allows you to simultaneously wait for input from the keyboard stdin handle, an I/O event from the camera, and a timeout value.