When Windows NT first hit the shelves, one of its key features was pre-emptive multitasking. Unlike Windows 3.x, in which a programmer is required to explicitly yield control of the processor from time to time, the NT operating system is free to stop and continue any running process, avoiding most lock-ups and freeing the programmer from sharing considerations. To improve the granularity of its multiprocess model, NT added threads, which are used to further subdivide the tasks running within a single process. Many programmers, anxious to leverage the Win32 improvements, began using threads heavily. Threads, however, can significantly complicate development and debugging, and the idea of using multiple threads in a user-facing Windows application became passe, except in special circumstances. But writing code that can be safely used by multiple threads is becoming critical again; this time it's driven by Web requirements.
The resurgence of concern about thread-safety has to do with the revolution in user-facing applications. In Windows-centric development, one could assume that only one user at a time would access the GUI part of an application. Typically, each user had a local copy of the user-interface software. And while threads could affect the responsiveness of an application, they usually didn’t improve throughput. Hence, thread safety wasn’t often a key issue in client-side applications.
As the marching orders arrive from both executives and developers to Webify applications, no one is excited about losing all of the investment in the current Windows user-interface. One popular way to Webify a Windows application is to replace the Windows dialog and display functions with code to generate and retrieve HTML. If you’ve kept your GUI code separate from your business logic, you are in better shape than many companies. If any of your business logic was designed to run on the client’s machine, it may not have been designed for thread safety. Web applications must be thread-safe because the Web server is able to simultaneously start any number of threads through the code.
In Windows NT, thread safety usually breaks into two discussions: operating system support for threads and COM support for threads. When you are writing a non-COM dynamic library, you will be responsible for ensuring the thread safety of your application using operating system synchronization mechanisms. In COM, Microsoft provides some help, although for the highest-performing applications you’ll have to resort to operating system synchronization mechanisms, as well.
In non-COM Win32, each process has one main thread and is able to create any number of additional threads. A thread is a new and independent execution path through a section of code. Threads are not explicitly synchronized with one another, nor do they have any default mechanism for communicating. If not explicitly managed, threads started by a process will continue to run until the process ends. Very few robust programs simply start a thread and forget about it -- most devise a control strategy based on synchronized communication with some shared memory location. If unsynchronized threads attempt to access the same memory, you may get a situation in which the memory is partially set by one thread and read by another thread. The threads can now mysteriously interchange part of their information, and a hard to find bug is introduced into the code.
COM’s threading model introduces the idea of apartments. The main idea behind COM apartments is to balance the ease of creating multithreaded objects with the performance penalty of letting Windows do the work. COM allows you to creatively group objects that need to work together into an apartment. As long as objects interact within a single apartment, no marshalling -- wrapping and unwrapping of procedure calls -- is required. Any communication outside of a single apartment requires marshalling. Why not just put everything in the same apartment? You can, except that everything within the same apartment must be thread-safe with respect to everything else in that apartment. The more object types within the apartment, the more work to ensure they are thread-safe.
In the COM threading world, you can either get easy thread-safety with mediocre performance -- single-threaded apartments -- or difficult-to-develop thread-safety with high performance -- multithreaded apartments. Or you can mix the two together. Visual Basic 6.0 provides easy access to the mediocre performance model. In a non-COM Windows NT application, you start immediately in the more difficult column, although NT has very good thread synchronization support. In all cases, avoiding global variables -- including C++ static variables in functions -- and passing your context around as either parameters or member variables will help keep applications threadable.
Regardless of whether you are doing Windows or Web development today, you should want to protect your investment by writing thread-friendly code. --Eric Binary Anderson is a development manager at PeopleSoft’s PeopleTools division (Pleasanton, Calif.) and has his own consulting business, Binary Solutions. Contact him at firstname.lastname@example.org.