FREE BOOK

Chapter 10: Processes, AppDomains,Contexts, and Threads

Posted by Apress Free Book | C#.NET February 02, 2009
In this Chapter you drill deeper into the constitution of a .NET executable host and come to understand the relationship between Win32 processes, application domains, contexts, and threads.

Reviewing Processes and Threads Under Traditional Win32

The concept of processes and threads has existed within Windows-based operating systems well before the release of the .NET platform. Simply put, process is the term used to describe the set of resources (such as external code libraries and the primary thread) as well as the necessary memory allocations used by a running application. For each *.exe loaded into memory, the operating system creates a separate and isolated memory partition (aka process) for use during its lifetime. Using this approach to application isolation, the result is a much more robust and stable runtime environment, given that the failure of one process does not effect the functioning of another.

Now, every Win32 process is assigned a unique process identifier (PID), and may be independently loaded and unloaded by the operating system as necessary (as well as programmatically using Win32 API calls). As you may be aware, the Processes tab of the Task Manager utility (activated via the Ctrl+Shift+Esc keystroke combination) allows you to view statistics regarding the set of processes running on a given machine, including its PID and image name (Figure 10-1). (If you do not see a PID column, select the View | Select Columns menu and check the PID box.)



Figure 10-1. The Windows Task Manager

Every Win32 process has at least one main "thread" that functions as the entry point for the application. Formally speaking, the first thread created by a process' entry point is termed the primary thread. Simply put, a thread is a specific path of execution within a Win32 process. Traditional Windows applications define the WinMain() method as the application's entry point. On the other hand, console application provides the main() method for the same purpose.

Processes that contain a single primary thread of execution are intrinsically "threadsafe," given the fact that there is only one thread that can access the data in the application at a given time. However, a single-threaded process (especially one that is GUIbased) will often appear a bit unresponsive to the user if this single thread is performing a complex operation (such as printing out a lengthy text file, performing an exotic calculation, or attempting to connect to a remote server thousands of miles away). Given this potential drawback of single-threaded applications, the Win32 API makes it is possible for the primary thread to spawn additional secondary threads (also termed worker threads) in the background, using a handful of Win32 API functions such as CreateThread(). Each thread (primary or secondary) becomes a unique path of execution in the process and has concurrent access to all shared points of data.

As you may have guessed, developers typically create additional threads to help improve the program's overall responsiveness. Multithreaded processes provide the illusion that numerous activities are happening at more or less the same time.

For example, an application may spawn a worker thread to perform a labor-intensive unit of work (again, such as printing a large text file). As this secondary thread is churning away, the main thread is still responsive to user input, which gives the entire process the potential of delivering greater performance. However, this may not actually be the case: using too many threads in a single process can actually degrade performance, as the CPU must switch between the active threads in the process (which takes time).

In reality, it is always worth keeping in mind that multithreading is most commonly an illusion provided by the operating system. Machines that host a single CPU do not have the ability to literally handle multiple threads at the same exact time. Rather, a single CPU will execute one thread for a unit of time (called a time-slice) based on the thread's priority level. When a thread's time-slice is up, the existing thread is suspended
to allow another thread to perform its business. For a thread to remember what was happening before it was kicked out of the way, each thread is given the ability to write to Thread Local Storage (TLS) and is provided with a separate call stack, as illustrated in
Figure 10-2.



Figure 10-2. The Win32 process / thread relationship

NOTE The newest Intel CPUs have an ability called hyperthreading that allows a single CPU to handle multiple threads simultaneously under certain circumstances. See http://www.intel.com/info/ hyperthreading for more details.

Interacting with Processes Under the .NET Platform

Although processes and threads are nothing new, the manner in which we interact with these primitives under the .NET platform has changed quite a bit (for the better). To pave the way to understanding the world of building multithreaded assemblies, let's begin by checking out how processes have been altered to accommodate the needs of the CLR.

The System.Diagnostics namespace defines a number of types that allow you to programmatically interact with processes and various diagnostic-related types such as the system event log and performance counters. For the purposes of this chapter, we are only concerned with the process-centric types, defined in Table 10-1.

Table 10-1. Select Members of the System.Diagnostics Namespace

Process-Centric Types of the System.Diagnostics Namespace Meaning in Life
Process The Process class provides access to local and remote processes and also allows you to programmatically start and stop processes.
ProcessModule This type represents a module (*.dll or *.exe) that is loaded into a particular process. Understand that the ProcessModule type can represent any module, COM-based, .NET-based, or traditional C-based binaries.
ProcessModuleCollection Provides a strongly typed collection of ProcessModule objects.
ProcessStartInfo Specifies a set of values used when starting a process via the Process.Start() method.
ProcessThread Represents a thread within a given process. ProcessThread is a type used to diagnose a process' thread set, and is not used to spawn new threads of execution within a process. As you will see later in this chapter, duties of this sort are the role of the types within the System.Threading namespace.
ProcessThreadCollection Provides a strongly typed collection of ProcessThread objects.

The System.Diagnostics.Process type allows you to identify the running processes on a given machine (local or remote). The Process class also provides members that allow you to programmatically start and terminate processes, establish a process' priority level, and obtain a list of active threads and/or loaded modules within a given process. Table 10-2 illustrates some (but not all) of the key members of System.Diagnostics.Process.

Table 10-2. Select Members of the Process Type

Member of System.Diagnostic.Process Meaning in Life
ExitCode This property gets the value that the associated process specified when it terminated. Do note that you will be required to handle the Exited event (for asynchronous notification) or call the WaitForExit() method (for synchronous notification) to obtain this value.
ExitTime This property gets the time stamp associated with the process that has terminated (represented with a DateTime type).
Handle Returns the handle associated to the process by the OS.
HandleCount Returns the number of handles opened by the process.
Id This property gets the process ID (PID) for the associated process.
MachineName This property gets the name of the computer the associated process is running on.
MainModule Gets the ProcessModule type that represents the main module for a given process.
MainWindowTitle MainWindowHandle MainWindowTitle gets the caption of the main window of the process (if the process does not have a main window, you receive an empty string). MainWindowHandle gets the underlying handle (represented via a System.IntPtr type) of the associated window. If the process does not have a main window, the IntPtr type is assigned the value System.IntPtr.Zero.
Modules Provides access to the strongly typed ProcessModuleCollection type, which represents the set of modules (*.dll or *.exe) loaded within the current process.
PriorityBoostEnabled Determines if the OS should temporarily boost the process if the main window has the focus.
PriorityClass Allows you to read or change the overall priority for the associated process.
ProcessName This property gets the name of the process (which as you would assume is the name of the application itself).
Responding This property gets a value indicating whether the user interface of the process is responding (or not).
StartTime This property gets the time that the associated process was started (via a DateTime type).
Threads This property gets the set of threads that are running in the associated process (represented via an array of ProcessThread types).
CloseMainWindow() Closes a process that has a user interface by sending a close message to its main window.
GetCurrentProcess() This static method returns a new Process type that represents the currently active process.
GetProcesses() This static method returns an array of new Process components running on a given machine.
Kill() Immediately stops the associated process. Start() Starts a process.
Start() Stats a Process.

 

Total Pages : 13 12345

comments