Multithreading: How to Use the Synchronization Classes
| Overview | How Do I | Sample
Synchronizing resource access between threads is a common problem when writing multithreaded applications. Having two or more threads simultaneously access the same data can lead to undesirable and unpredictable results. For example, one thread could be updating the contents of a structure while another thread is reading the contents of the same structure. It is unknown what data the reading thread will receive: the old data, the newly written data, or possibly a mixture of both. MFC provides a number of synchronization and synchronization access classes to aid in solving this problem. This article explains the classes available and how to use them to create thread-safe classes in a typical multithreaded application.
A typical multithreaded application has a class that represents a resource to be shared among threads. A properly designed, fully thread-safe class does not require you to call any synchronization functions. Everything is handled internally to the class, allowing you to concentrate on how to best use the class, not about how it might get corrupted. The best technique for creating a fully thread-safe class is to merge the synchronization class into the resource class. Merging the synchronization classes into the shared class is a straightforward process.
As an example, take an application that maintains a linked list of accounts. This application allows up to three accounts to be examined in separate windows, but only one can be updated at any particular time. When an account is updated, the updated data is sent over the network to a data archive.
This example application uses all three types of synchronization classes. Because it allows up to three accounts to be examined at one time, it uses to limit access to three view objects. When an attempt to view a fourth account occurs, the application either waits until one of the first three windows closes or it fails. When an account is updated, the application uses to ensure that only one account is updated at a time. After the update succeeds, it signals , which releases a thread waiting for the event to be signaled. This thread sends the new data to the data archive.
Designing a Thread-Safe Class
To make a class fully thread-safe, first add the appropriate synchronization class to the shared classes as a data member. In the previous account-management example, a CSemaphore data member would be added to the view class, a CCriticalSection data member would be added to the linked-list class, and a CEvent data member would be added to the data storage class.
Next, add synchronization calls to the appropriate member functions of each thread-safe class. This means that all member functions that modify the data in the class or access a controlled resource should create either a ****or ****object and call that object’s Lock function. When the lock object goes out of scope and is destroyed, the object’s destructor calls Unlock for you, releasing the resource. Of course, you can call Unlock directly if you wish.
Designing your thread-safe class in this fashion allows it to be used in a multithreaded application as easily as a non-thread-safe class, but with complete safety. Encapsulating the synchronization object and synchronization access object into the resource’s class provides all the benefits of fully thread-safe programming without the drawback of maintaining synchronization code.
The drawbacks to this approach are that the class will be slightly slower than the same class without the synchronization objects added. Also, if there is a chance that more than one thread may delete the object, the merged approach might not always work. In this situation, it is better to maintain separate synchronization objects.
For example code that uses the synchronization classes, see the MFC sample programs and . These and other MFC sample programs can be found in Visual C++ Samples.
For information on determining which synchronization class to use in different situations, see the article Multithreading: When to Use the Synchronization Classes. For more information on synchronization, see in the Win32 Programmer’s Reference of the Win32 SDK. For more information on multithreading support in MFC, see the article Multithreading with C++ and MFC.