Multithreading: Programming Tips
The new home for Visual Studio documentation is Visual Studio 2017 Documentation on docs.microsoft.com.
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 topic explains techniques for avoiding potential problems when programming multithreaded applications with the Microsoft Foundation Class (MFC) library.
Accessing Objects from Multiple Threads
Accessing MFC Objects from Non-MFC Threads
Windows Handle Maps
Communicating Between Threads
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 about critical sections and other related objects, see Synchronization in the Windows 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 CWinThread 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 Multithreading: Creating User-Interface Threads or Multithreading: Creating Worker Threads. 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 help maintain 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 has no effect at all, because the map from CWnd
objects to HWND
s is local to the primary thread. This means that one thread might have a mapping from a Windows handle to a C++ object, but another thread might 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 about handle maps, see Technical Note 3. For more information about thread local storage, see Thread Local Storage and Using Thread Local Storage in the Windows SDK.
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 Multithreading: How to Use the Synchronization Classes and Multithreading: When to Use the Synchronization Classes. For more information about these objects, see Synchronization in the Windows SDK.