Concurrency Design Dimensions
5.2 Processes versus Threads
Section described the of concurrent servers, which can be implemented using multiple processes or multiple threads. The primary tradeoffs in this dimension involve robustness, efficiency, and scalability.
Multiprocessing. A process is the OS entity that provides the context for executing program instructions. Each process manages certain resources, such as virtual memory, I/O and signal handlers, and is protected from other OS processes via memory management unit hardware.
Processes created by on UNIX and by CreateProcess on Win32 execute concurrently in different address spaces than their callers. These mechanisms are examined closely in Chapter 8.
operating systems, such as BSD UNIX [MBKQ96], provided processes with just one thread of control. This single-threaded process model can enhance robustness since processes can't interfere with one another without explicit programmer intervention. For example, pro-cesses can collaborate with each other only via shared memory or local IPC mechanisms, as shown in Figure 5.2
It's hard to use single-threaded processes, however, to develop certain types of applications, particularly high-performance or real-time servers.
110 Concurrency Design
Servers that need to communicate with each other or respond to manage-ment requests must use some form of which adds to their complexity.
It's also hard to exert efficient, fine-grain control over scheduling and pro-cess priority using multipropro-cessing.
Multithreading. To alleviate the problems with processes outlined above, most OS platforms now support multiple threads within a process. A thread is a single sequence of instruction steps executed in the context of a process's protection domain, as shown in Figure 5.2 (2). In addition to an instruction pointer, a thread manages certain resources, such as a run-time stack of function activation records, a set of registers, signal masks, priorities, and thread-specific data. If multiple CPUs are available, services in multithreaded servers can execute in parallel On many ver-sions of UNIX, threads are spawned by on Win32 they are spawned by CreateThread
Implementing concurrent networked applications that perform multi-ple operations in separate threads rather than in separate processes can reduce the following sources of concurrency overhead:
• Thread creation and context switching. Since threads maintain less state than processes, thread creation and context switching overhead can be lower than the corresponding process lifecycle activities. For exam-ple, process-wide resources, such as virtual address mappings and caches, needn't change when switching between threads in a process.
• Synchronization. It may not be necessary to switch between ker-nel mode and user mode when scheduling and executing an application thread. Likewise, intraprocess synchronization is often less expensive than interprocess synchronization because the objects being synchronized are local to a process and may therefore require no kernel intervention. In contrast, interprocess thread synchronization generally involves the OS kernel.
• Data copying. Threads can share information using process-local memory, which has the following benefits:
It's often more efficient than using shared memory or local IPC mech-anisms to communicate between processes because data needn't be copied through the kernel.
2. It's easier to use C++ objects in process-local memory since there's no problem with class virtual table layouts (see Sidebar 3 on page 30).
Section 5.2 Processes versus Threads 111
For example, cooperating database services that reference common data structures resident in process-local memory are simpler and more efficient to implement with multiple threads than with multiple processes.
As a result of these optimizations, multithreading can often improve ap-plication performance significantly. For example, applications can benefit from multithreading since compute-intensive services can be overlapped with disk and network operations. Just because an OS plat-form supports threads, however, doesn't imply that all applications should be multithreaded. In particular, the following limitations arise when using multithreading to implement concurrent applications:
• Performance degradation. A common misconception is that thread-ing inherently improves application performance. Many times, however, threading doesn't improve performance for several reasons, including:
1. Compute-bound applications on a uniprocessor won't benefit from multithreading since computations and communication can't run in parallel.
2. Fine-grained locking strategies can yield high synchronization over-head, which prevents applications from fully exploiting the benefits of parallel processing
• Reduced robustness. To reduce context switching and synchroniza-tion overhead, threads receive little or no MMU protecsynchroniza-tion from each other.
Executing all tasks via threads within a single process address space can reduce application robustness for several reasons, including:
Separate threads within the same process address space aren't well protected from each other. One faulty service in a process can there-fore corrupt global data shared by services running on other threads in the process. This, in turn, may produce incorrect results, crash an entire process, or cause an application to hang indefinitely.
2. Certain OS functions invoked in one thread can have undesirable side effects on an entire process; for example, the UNIX exit and Win32 ExitProcess functions have the side effect of terminating all the threads within a process.
• Lack of fine-grained access control. On most operating systems, a process is the granularity of access control. Another limitation with mul-tithreading, therefore, is that all threads within a process share the same
CHAPTER 5 Concurrency Design Dimensions
user ID and access privileges to flies and other protected resources. To pre-vent accidental or intentional access to unauthorized resources, network services, such as TELNET, that base their security mechanisms on process ownership often run in separate processes.
Logging service The concurrent implementations of the logging server in our networked logging service can be implemented in a variety of ways.
Chapters 8 and 9 use multiple processes and multiple threads, respec-tively, to implement concurrent logging servers.