Multithreading: Programming Tips

Visual Studio 6.0 Do ISample

Multithreaded applications require stricter care than single-threaded applications when accessing data. Because there are multiple, independent paths of execution in use simultaneously in multithreaded applications, either the algorithms, the data, or both must be aware that data could be used by more than one thread at a time. This article explains techniques for avoiding potential problems when programming multithreaded applications with the Microsoft Foundation Class Library (MFC).

Accessing Objects from Multiple Threads

For size and performance reasons, MFC objects are not thread-safe at the object level, only at the class level. This means that you can have two separate threads manipulating two different CString objects, but not two threads manipulating the same CString object. If you absolutely must have multiple threads manipulating the same object, protect such access with appropriate Win32 synchronization mechanisms, such as critical sections. For more information on critical sections and other related objects, see in the Win32 SDK.

The class library uses critical sections internally to protect global data structures, such as those used by the debug memory allocation.

Accessing MFC Objects from Non-MFC Threads

If you have a multithreaded application that creates a thread in a way other than using a object, you cannot access other MFC objects from that thread. In other words, if you want to access any MFC object from a secondary thread, you must create that thread with one of the methods described in the Multithreading: Creating User-Interface Threads or Multithreading: Creating Worker Threads articles. These methods are the only ones that allow the class library to initialize the internal variables necessary to handle multithreaded applications.

Windows Handle Maps

As a general rule, a thread can access only MFC objects that it created. This is because temporary and permanent Windows handle maps are kept in thread local storage to ensure protection from simultaneous access from multiple threads. For example, a worker thread cannot perform a calculation and then call a document’s UpdateAllViews member function to have the windows that contain views on the new data modified. This will have no effect at all, because the map from CWnd objects to HWNDs is local to the primary thread. This means that one thread may have a mapping from a Windows handle to a C++ object, but another thread may map that same handle to a different C++ object. Changes made in one thread would not be reflected in the other.

There are several ways around this problem. The first is to pass individual handles (such as an HWND) rather than C++ objects to the worker thread. The worker thread then adds these objects to its temporary map by calling the appropriate FromHandle member function. You could also add the object to the thread’s permanent map by calling Attach, but this should be done only if you are guaranteed that the object will exist longer than the thread.

Another method is to create new user-defined messages corresponding to the different tasks your worker threads will be performing and post these messages to the application’s main window using ::PostMessage. This method of communication is similar to two different applications conversing except that both threads are executing in the same address space.

For more information on handle maps, see . For more information on thread local storage, see and in the Win32 Programmer’s Reference.

Communicating Between Threads

MFC provides a number of classes that allow threads to synchronize access to objects to maintain thread safety. Usage of these classes is described in the articles Multithreading: How to Use the Synchronization Classes and Multithreading: When to Use the Synchronization Classes. More information on these objects can be found in in the Win32 Programmer’s Reference.