When developing multi-threaded applications you can never be sure when and in which order threads run, so you have to synchronize access to shared resources. Otherwise, for example, one thread could modify a piece of data while another thread reads or modifies it. This can give you inconsistent data or, especially when resource allocation/deallocation is involved, crashes.
Threads and thread synchronization is a vast topic and handled differently on every OS. There is POSIX threads (Pthreads) for Unixes and the Thread API for Windows. C++0x is supposed to have a std::thread class, but support (at least in Visual Studio 2010 – shame on you MS) is missing. But there are other portable thread implementations, notably boost::thread and TinyThread++. Use those if you can, licenses permit derivative work for non-commercial and commercial usage without having to contribute sources back.
Now to CriticalSections. They are supposed to be faster than Mutexes on Windows, because they use processor-level functions rather than OS-level functions, but they can only be used by threads of the same process.
The following code shows how a critical section (SynchronizedData::_cs) can be used to provide synchronized access to a resource(SynchronizedData::_data).
class SynchronizedData
{
public:
int _data;
CRITICAL_SECTION _cs;
SynchronizedData()
: _data(0) {
InitializeCriticalSection(&_cs);
};
bool lock() {
__try {
EnterCriticalSection(&_cs);
return true;
}
__finally {
LeaveCriticalSection(&_cs);
}
return false;
};
void unlock() {
LeaveCriticalSection(&_cs);
}
~SynchronizedData() {
DeleteCriticalSection(&_cs);
};
};
class SomeThread
{
HANDLE _handle; //thread handle of object
bool _run; //true while the thread is running. set to false to exit threadLoop()
SynchronizedData & _sdata; //shared data object reference
public:
int increaseCount; //just here to count how many times this object has increased the data
SomeThread(SynchronizedData & sdata)
: _handle(NULL), _run(true), _sdata(sdata), increaseCount(0) {
//create new thread passing this object as a parameter
CreateThread(NULL, 0, _handle, &this, 0, nullptr);
};
DWORD WINAPI threadLoop(LPVOID parameter) {
//we get the this pointer of the object that created the thread here (s.a.)
SomeThread * thread = (SomeThread *)parameter;
if (NULL != thread) {
while(_run) {
//try to lock resource for us
if (thread->_sdata.lock()) {
//resource locked. we can use it
thread->_sdata._data++;
//finally unlock resource again
thread->_sdata.unlock();
increaseCount++;
}
Sleep(100);
}
}
};
void stop() {
if (_run) {
//set _run to false to gracefully quit threadLoop()
_run = false;
//and wait for the thread to quit
WaitForSingleObject(_handle, INFINITE);
//close thread handle
CloseHandle(_handle);
}
}
~SomeThread() {
stop();
};
};
int main() {
//create data
SynchronizedData sdata;
//create threads and run them
SomeThread thread1(sdata);
SomeThread thread2(sdata);
//idle around in this thread, printing the value of data every second
while(true) {
//try to lock resource for us
if (sdata.lock()) {
//resource locked. we can use it
printf("data: %d, thread1 count: %d, thread2 count: %d\n", sdata._data, thread1.increaseCount, thread2.increaseCount);
//finally unlock resource again
sdata.unlock();
}
Sleep(1000);
}
//before exiting stop threads
thread1.stop();
thread2.stop();
return 0;
}