Recently, my brother asked me whether there was an easy way to create a C++ class that facilitated object orientated threading. I have written many multi-threaded libraries in the past; however, they are all in C. C has always been my language of choice for low-level programming; I use C++ for GUI development. Although there are many excellent examples of object-orientated threading on CodeGuru, none of the classes introduced suited all of my brother's needs and my curiosities. He wanted a thread class that had the following attributes:
- It supports both event driven and interval based asynchronous threading.
- It supports both homogeneous and specialized threading.
- It provides a FCFS (First Come First Serve) stack-queue for posting and handling multiple tasks.
- It is portable.
- It is simple to implement.
To support the new class, CThread, other supporting classes were also developed. These classes included the CMutexClass, CEventClass, and CTask classes. The CMutexClass and CEventClass provide resource management while the CTask class is a base class for deriving classes that support homogeneous asynchronous threading.
What Is Threading?
Every process has at least one thread of control and every process can perform at least one task at a time. A process that has more than one thread of control defines a multi-threaded process. A multi-threaded process allows multiple tasks to run asynchronously from within the environment of the process.
Resource Management—Thread Synchronization
Because threads within a multi-threaded process share the same resources, OS level control mechanisms are necessary to insure data integrity. A loss of data integrity occurs when one thread is modifying a variable while another thread is attempting to read it or two threads are attempting to modify the same variable at the same time. To prevent this scenario, the OS provides a Mutual Exclusion Object known in short as a mutex. In multi-threaded applications, mutexes, deployed programmatically, prevent multiple threads from accessing a single resource at the same time. When a thread needs access to a resource, it must first acquire a mutex. Once a thread has acquired a mutex, other threads attempting to acquire the same mutex are blocked and placed in a low-CPU usage wait state. Once a thread has completed data access, it releases the corresponding mutex; this allows other threads to acquire it and access the corresponding data.
Poor implementations of mutexes can result in resource starvation, also known as deadlock. Resource starvation occurs when one or more threads are competing for the same resource. Deadlock can also occur if a thread attempts to acquire a mutex twice.
|Thread A||Thread B|
|Acquires mutex(1) to modify data item 1||Acquires mutex(2) to modify data item 2|
|Wants mutex(2) to view data item 2||Wants mutex(1) to view data item 1|
Deadlock occurs in the example above because Thread A is blocked trying to acquire mutex(2), which is held by thread B. Thread B is blocked trying to acquire mutex(1) which is blocked by Thread A.
Like mutexes, condition variables, in UNIX, are another form of synchronization mechanism. Condition variables allow threads to rendezvous. They allow one thread to notify another that a change has occurred. In Windows, these are events.
Operating System Calls
The following table is a list of the various functions used to implement threading in the CMutexClass, CEventClass, CTask, and CThread classes.
|Function||OS||Description||Class Used in|
|CreateThread||Windows||Creates a Windows thread||CThread|
|pthread_create||UNIX - POSIX THREADS||Creates a UNIX thread||CThread|
|pthread_join||UNIX - POSIX THREADS||Waits for a UNIX thread to terminate||CThread|
|pthread_attr_init||UNIX - POSIX THREADS||Sets a thread attribute structure to default||CThread|
|pthread_attr_setstacksize||UNIX - POSIX THREADS||Sets the stack size value of the thread attribute structure||CThread|
|WaitForSingleObject||Windows||Waits for an object to be signaled||CThread, CMutexClass, CEventClass|
|CreateMutex||Windows||Creates a named or unnamed mutex||CMutexClass|
|CloseHandle||Windows||Releases resources alloacate to a Windows handle||CMutexClass, CEventClass, CThread|
|ReleaseMutex||Windows||Releases a prevously acquired mutex locked by WaitForSingleObject||CMutexClass, CEventClass|
|pthread_mutexattr_init||UNIX - POSIX THREADS||Initializes a mutex attribute structure||CMutexClass, CEventClass|
|pthread_mutex_init||UNIX - POSIX THREADS||Initializes a mutex using a provided attribute structure||CMutexClass, CEventClass|
|pthread_mutex_lock||UNIX - POSIX THREADS||Locks a mutex||CMutexClass, CEventClass|
|pthread_mutex_unlock||UNIX - POSIX THREADS||Unlocks a mutex previously locked by pthread_mutex_lock||CMutexClass, CEventClass|
|pthread_mutex_destroy||UNIX - POSIX THREADS||Releases resources allocated to a mutex||CMutexClass, CEventClass|
|CreateEvent||Windows||Creates a Windows event object||CEventClass|
|SetEvent||Windows||Sets a Windows event object to signaled||CEventClass|
|pthread_cond_signal||UNIX - POSIX THREADS||Unblocks a thread blocked on pthread_cond_wait||CEventClass|
|pthread_cond_wait||UNIX - POSIX THREADS||Blocks on a condition variable||CEventClass|
|pthread_cond_init||UNIX - POSIX THREADS||Initializes a condition variable||CEventClass|