Using CriticalSections to synchronize shared resource access

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, NULL);
    };

    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;
}

Comments