Chapter 4. Threads, Processes, and Synchronization

One of the strengths of the Microsoft Windows operating system is that it allows many programs (processes) to run concurrently and allows each process to perform many tasks concurrently (using multiple threads). When you run an executable application, a new process is created. The process isolates your application from other programs running on the computer. The process provides the application with its own virtual memory and its own copies of any libraries it needs to run, allowing your application to execute as if it were the only application running on the machine.

Along with the process, an initial thread is created that runs your Main method. In single-threaded applications, this one thread steps through your code and sequentially performs each instruction. If an operation takes time to complete, such as reading a file from the Internet or doing a complex calculation, the application will be unresponsive (will block) until the operation is finished, at which point the thread will continue with the next operation in your program.

To avoid blocking, the main thread can create additional threads and specify which code each should start running. As a result, many threads may be running in your application's process, each running (potentially) different code and performing different operations seemingly simultaneously. In reality, unless you have multiple processors (or a single multicore processor) in your computer, the threads are not really running simultaneously. Instead, the operating system coordinates and schedules the execution of all threads across all processes; each thread is given a tiny portion (or time slice) of the processor's time, which gives the impression they are executing at the same time.

The difficulty of having multiple threads executing within your application arises when those threads need to access shared data and resources. If multiple threads are changing an object's state or writing to a file at the same time, your data will quickly become corrupt. To avoid problems, you must synchronize the threads to make sure they each get a chance to access the resource, but only one at a time. Synchronization is also important when waiting for a number of threads to reach a certain point of execution before proceeding with a different task, and for controlling the number of threads that are at any given time actively performing a task—perhaps processing requests from client applications.

.NET 4.0 includes two mechanisms for creating multithreaded applications. The "classic" approach is the one that has been included in .NET since version 1.0, in which the programmer takes responsibility for creating and managing threads directly. The Task Parallel Library is newly added and manages threads automatically but reduces the developer's control over the application.

For information about the Task Parallel Library, please see Chapter 15. This chapter describes how to control processes and threads using the classic techniques, which are more complex, but offer a greater degree of control. Specifically, the recipes in this chapter describe how to do the following:

  • Execute code in independent threads using features including the thread pool, asynchronous method invocation, and timers (recipes 4-1 through 4-6)

  • Synchronize the execution of multiple threads using a host of synchronization techniques including monitors, events, mutexes, and semaphores (recipes 4-7 and 4-11)

  • Terminate threads and know when threads have terminated (recipes 4-12 and 4-13)

  • Create thread-safe instances of the .NET collection classes (recipe 4-14)

  • Start and stop applications running in new processes (recipes 4-15 and 4-16)

  • Ensure that only one instance of an application is able to run at any given time (recipe 4-17)

As you will see in this chapter, delegates are used extensively in multithreaded programs to wrap the method that a thread should execute or that should act as a callback when an asynchronous operation is complete. Prior to C# 2.0, it was necessary to

  1. Declare a method that matches the signature of the required delegate

  2. Create a delegate instance of the required type by passing it the name of the method

  3. Pass the delegate instance to the new thread or asynchronous operation

C# 2.0 added two important new features that simplify the code you must write when using delegates:

  • First, you no longer need to create a delegate instance to wrap the method you want to execute. You can pass a method name where a delegate is expected, and as long as the method signature is correct, the compiler infers the need for the delegate and creates it automatically. This is a compiler enhancement only—the intermediate language (IL) generated is as if the appropriate delegate had been instantiated. Recipes 4-1 and 4-2 (along with many others) demonstrate how to use this capability.

  • Second, you no longer need to explicitly declare a method for use with the delegate. Instead, you can provide an anonymous method wherever a delegate is required. In effect, you actually write the method code at the point where you would usually pass the method name (or delegate instance). The only difference is that you use the keyword delegate instead of giving the method a name. This approach can reduce the need to implement methods solely for use as callbacks and event handlers, which reduces code clutter, but it can quickly become confusing if the anonymous method is longer than a couple of lines of code. Recipes 4-3 and 4-4 demonstrate how to use anonymous methods.

Execute a Method Using the Thread Pool

Problem

You need to execute a task using a thread from the runtime's thread pool.

Solution

Declare a method containing the code you want to execute. The method's signature must match that defined by the System.Threading.WaitCallback delegate; that is, it must return void and take a single object argument. Call the static method QueueUserWorkItem of the System.Threading.ThreadPool class, passing it your method name. The runtime will queue your method and execute it when a thread-pool thread becomes available.

How It Works

Applications that use many short-lived threads or maintain large numbers of concurrent threads can suffer performance degradation because of the overhead associated with the creation, operation, and destruction of threads. In addition, it is common in multithreaded systems for threads to sit idle a large portion of the time while they wait for the appropriate conditions to trigger their execution. Using a thread pool provides a common solution to improve the scalability, efficiency, and performance of multithreaded systems.

The .NET Framework provides a simple thread-pool implementation accessible through the members of the ThreadPool static class. The QueueUserWorkItem method allows you to execute a method using a thread-pool thread by placing a work item on a queue. As a thread from the thread pool becomes available, it takes the next work item from the queue and executes it. The thread performs the work assigned to it, and when it is finished, instead of terminating, the thread returns to the thread pool and takes the next work item from the work queue.

Tip

If you need to execute a method with a signature that does not match the WaitCallback delegate, then you must use one of the other techniques described in this chapter. See recipe 4-2 or 4-6.

The Code

The following example demonstrates how to use the ThreadPool class to execute a method named DisplayMessage. The example passes DisplayMessage to the thread pool twice, first with no arguments and then with a MessageInfo object, which allows you to control which message the new thread will display:

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_01
    {

        // A private class used to pass data to the DisplayMessage method when it is
        // executed using the thread pool.
        private class MessageInfo
        {
            private int iterations;
            private string message;

            // A constructor that takes configuration settings for the thread.
            public MessageInfo(int iterations, string message)
            {
                this.iterations = iterations;
                this.message = message;
            }

            // Properties to retrieve configuration settings.
            public int Iterations { get { return iterations; } }
            public string Message { get { return message; } }
        }

        // A method that conforms to the System.Threading.WaitCallback delegate
        // signature. Displays a message to the console.
        public static void DisplayMessage(object state)
        {
            // Safely cast the state argument to a MessageInfo object.
            MessageInfo config = state as MessageInfo;

            // If the config argument is null, no arguments were passed to
            // the ThreadPool.QueueUserWorkItem method; use default values.
            if (config == null)
            {
                // Display a fixed message to the console three times.
                for (int count = 0; count < 3; count++)
                {
                    Console.WriteLine("A thread pool example.");

                    // Sleep for the purpose of demonstration. Avoid sleeping
                    // on thread-pool threads in real applications.
                    Thread.Sleep(1000);
                }

            }
            else
            {
// Display the specified message the specified number of times.
                for (int count = 0; count < config.Iterations; count++)
                {
                    Console.WriteLine(config.Message);

                    // Sleep for the purpose of demonstration. Avoid sleeping
                    // on thread-pool threads in real applications.
                    Thread.Sleep(1000);
                }
            }
        }

        public static void Main()
        {
            // Execute DisplayMessage using the thread pool and no arguments.
            ThreadPool.QueueUserWorkItem(DisplayMessage);

            // Create a MessageInfo object to pass to the DisplayMessage method.
            MessageInfo info = new MessageInfo(5,
                "A thread pool example with arguments.");

            // Set the max number of threads.
            ThreadPool.SetMaxThreads(2, 2);

            // Execute DisplayMessage using the thread pool and providing an
            // argument.
            ThreadPool.QueueUserWorkItem(DisplayMessage, info);

            // Wait to continue.
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Notes

Using the runtime's thread pool simplifies multithreaded programming dramatically; however, be aware that the implementation is a simple, general-purpose thread pool. Before deciding to use the thread pool, consider the following points:

  • Each process has one thread pool, which supports by default a maximum of 25 concurrent threads per processor. You can change the maximum number of threads using the method ThreadPool.SetMaxThreads, but some runtime hosts (Internet Information Services [IIS] and SQL Server, for example) will limit the maximum number of threads and may not allow the default value to be changed at all.

  • As well as allowing you to use the thread pool to execute code directly, the runtime uses the thread pool for other purposes internally. This includes the asynchronous execution of methods (see recipe 4-2), execution of timer events (see recipes 4-3 and 4-4), and execution of wait-based methods (see recipe 4-5). All of these uses can lead to heavy contention for the thread-pool threads, meaning that the work queue can become very long. Although the work queue's maximum length is limited only by the amount of memory available to the runtime's process, an excessively long queue will result in long delays before queued work items are executed. The ThreadPool.GetAvailableThreads method returns the number of threads currently available in the thread pool. This can be useful in determining whether your application is placing too much load on the thread pool, indicating that you should increase the number of available threads using the ThreadPool.SetMaxThreads method.

  • Where possible, avoid using the thread pool to execute long-running processes. The limited number of threads in the thread pool means that a handful of threads tied up with long-running processes can significantly affect the overall performance of the thread pool. Specifically, you should avoid putting thread-pool threads to sleep for any length of time.

  • Thread-pool threads are background threads. You can configure threads as either foreground threads or background threads. Foreground and background threads are identical except that a background thread will not keep an application process alive. Therefore, your application will terminate automatically when the last foreground thread of your application terminates.

  • You have no control over the scheduling of thread-pool threads, and you cannot prioritize work items. The thread pool handles each work item in the sequence in which you add it to the work queue.

  • Once a work item is queued, it cannot be canceled or stopped.

  • Do not try to use thread-pool threads to directly update or manipulate Windows Forms controls, because they can be updated only by the thread that created them. Instead, use the controls' Dispatcher property—see the .NET Framework documentation for details.

Execute a Method Asynchronously

Problem

You need to start execution of a method and continue with other tasks while the method runs on a separate thread. After the method completes, you need to retrieve the method's return value.

Solution

Declare a delegate with the same signature as the method you want to execute. Create an instance of the delegate that references the method. Call the BeginInvoke method of the delegate instance to start executing your method. Use the EndInvoke method to determine the method's status as well as obtain the method's return value if complete.

How It Works

Typically, when you invoke a method, you do so synchronously, meaning that the calling code blocks until the method is complete. Most of the time, this is the expected, desired behavior because your code requires the operation to complete before it can continue. However, sometimes it is useful to execute a method asynchronously, meaning that you start the method in a separate thread and then continue with other operations.

The .NET Framework implements an asynchronous execution pattern that allows you to call any method asynchronously using a delegate. When you declare and compile a delegate, the compiler automatically generates two methods that support asynchronous execution: BeginInvoke and EndInvoke. When you call BeginInvoke on a delegate instance, the method referenced by the delegate is queued for asynchronous execution. Control returns to the caller immediately, and the referenced method executes in the context of the first available thread-pool thread.

The signature of the BeginInvoke method includes the same arguments as those specified by the delegate signature, followed by two additional arguments to support asynchronous completion. These additional arguments are as follows:

  • A System.AsyncCallback delegate instance that references a method that the runtime will call when the asynchronous method completes. The method will be executed by a thread-pool thread. Passing null means no method is called and means you must use another mechanism (discussed later in this recipe) to determine when the asynchronous method is complete.

  • A reference to an object that the runtime associates with the asynchronous operation for you. The asynchronous method does not use or have access to this object, but it is available to your code when the method completes, allowing you to associate useful state information with an asynchronous operation. For example, this object allows you to map results against initiated operations in situations where you initiate many asynchronous operations that use a common callback method to perform completion.

The EndInvoke method allows you to retrieve the return value of a method that was executed asynchronously, but you must first determine when it has finished. If your asynchronous method threw an exception, it will be rethrown so that you can handle it when you call EndInvoke. Here are the four techniques for determining whether an asynchronous method has finished:

  • Blocking stops the execution of the current thread until the asynchronous method completes execution. In effect, this is much the same as synchronous execution. However, you have the flexibility to decide exactly when your code enters the blocked state, giving you the opportunity to perform some additional processing before blocking.

  • Polling involves repeatedly testing the state of an asynchronous method to determine whether it is complete. This is a simple technique and is not particularly efficient from a processing perspective. You should avoid tight loops that consume processor time; it is best to put the polling thread to sleep for a period using Thread.Sleep between completion tests. Because polling involves maintaining a loop, the actions of the waiting thread are limited, but you can easily update some kind of progress indicator.

  • Waiting depends on the AsyncWaitHandle property of the IAsyncResult returned by BeginInvoke. This object derives from the System.Threading.WaitHandle class signals when the asynchronous method completes. Waiting is a more efficient version of polling, and in addition allows you to wait for multiple asynchronous methods to complete. You can also specify timeout values to allow your waiting thread to notify a failure if the asynchronous method takes too long or if you want to periodically update a status indicator.

  • A callback is a method that the runtime calls when an asynchronous operation completes. The calling code does not have to take any steps to determine when the asynchronous method is complete and is free to continue with other processing. Callbacks provide the greatest flexibility but also introduce the greatest complexity, especially if you have many asynchronous operations active concurrently that all use the same callback. In such cases, you must use appropriate state objects as the last parameter of BeginInvoke to match the completed methods against those you initiated.

Warning

Even if you do not want to handle the return value of your asynchronous method, you should call EndInvoke; otherwise, you risk leaking memory each time you initiate an asynchronous call using BeginInvoke.

The Code

The following code demonstrates how to use the asynchronous execution pattern. It uses a delegate named AsyncExampleDelegate to execute a method named LongRunningMethod asynchronously. LongRunningMethod simulates a long-running method using a configurable delay (produced using Thread.Sleep). The example contains the following five methods, which demonstrate the various approaches to handling asynchronous method completion:

  • The BlockingExample method executes LongRunningMethod asynchronously and continues with a limited set of processing. Once this processing is complete, BlockingExample blocks until LongRunningMethod completes. To block, BlockingExample calls the EndInvoke method of the AsyncExampleDelegate delegate instance. If LongRunningMethod has already finished, EndInvoke returns immediately; otherwise, BlockingExample blocks until LongRunningMethod completes.

  • The PollingExample method executes LongRunningMethod asynchronously and then enters a polling loop until LongRunningMethod completes. PollingExample tests the IsCompleted property of the IAsyncResult instance returned by BeginInvoke to determine whether LongRunningMethod is complete; otherwise, PollingExample calls Thread.Sleep.

  • The WaitingExample method executes LongRunningMethod asynchronously and then waits until LongRunningMethod completes. WaitingExample uses the AsyncWaitHandle property of the IAsyncResult instance returned by BeginInvoke to obtain a WaitHandle and then calls its WaitOne method. Using a timeout allows WaitingExample to break out of waiting in order to perform other processing or to fail completely if the asynchronous method is taking too long.

  • The WaitAllExample method executes LongRunningMethod asynchronously multiple times and then uses an array of WaitHandle objects to wait efficiently until all the methods are complete.

  • The CallbackExample method executes LongRunningMethod asynchronously and passes an AsyncCallback delegate instance (that references the CallbackHandler method) to the BeginInvoke method. The referenced CallbackHandler method is called automatically when the asynchronous LongRunningMethod completes, leaving the CallbackExample method free to continue processing.

Note

For the purpose of demonstrating the various synchronization techniques, the example performs several tasks that should be avoided when using the thread pool, including putting thread-pool threads to sleep and calling long-running methods. See recipe 4-1 for more suggestions on using the thread pool appropriately.

using System;
using System.Threading;
using System.Collections;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_02
    {
        // A utility method for displaying useful trace information to the
        // console along with details of the current thread.
        private static void TraceMsg(DateTime time, string msg)
        {
            Console.WriteLine("[{0,3}/{1}] - {2} : {3}",
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsThreadPoolThread ? "pool" : "fore",
                time.ToString("HH:mm:ss.ffff"), msg);
        }

        // A delegate that allows you to perform asynchronous execution of
        // LongRunningMethod.
public delegate DateTime AsyncExampleDelegate(int delay, string name);

        // A simulated long-running method.
        public static DateTime LongRunningMethod(int delay, string name)
        {
            TraceMsg(DateTime.Now, name + " example - thread starting.");

            // Simulate time-consuming processing.
            Thread.Sleep(delay);

            TraceMsg(DateTime.Now, name + " example - thread stopping.");

            // Return the method's completion time.
            return DateTime.Now;
        }

        // This method executes LongRunningMethod asynchronously and continues
        // with other processing. Once the processing is complete, the method
        // blocks until LongRunningMethod completes.
        public static void BlockingExample()
        {
            Console.WriteLine(Environment.NewLine +
                "*** Running Blocking Example ***");

            // Invoke LongRunningMethod asynchronously. Pass null for both the
            // callback delegate and the asynchronous state object.
            AsyncExampleDelegate longRunningMethod = LongRunningMethod;

            IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000,
                "Blocking", null, null);

            // Perform other processing until ready to block.
            for (int count = 0; count < 3; count++)
            {
                TraceMsg(DateTime.Now,
                    "Continue processing until ready to block...");

                Thread.Sleep(200);
            }

            // Block until the asynchronous method completes.
            TraceMsg(DateTime.Now,
                "Blocking until method is complete...");

            // Obtain the completion data for the asynchronous method.
            DateTime completion = DateTime.MinValue;

            try
            {
                completion = longRunningMethod.EndInvoke(asyncResult);
            }
catch
            {
                // Catch and handle those exceptions you would if calling
                // LongRunningMethod directly.
            }

            // Display completion information.
            TraceMsg(completion,"Blocking example complete.");
        }

        // This method executes LongRunningMethod asynchronously and then
        // enters a polling loop until LongRunningMethod completes.
        public static void PollingExample()
        {
            Console.WriteLine(Environment.NewLine +
                "*** Running Polling Example ***");

            // Invoke LongRunningMethod asynchronously. Pass null for both the
            // callback delegate and the asynchronous state object.
            AsyncExampleDelegate longRunningMethod = LongRunningMethod;

            IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000,
                "Polling", null, null);

            // Poll the asynchronous method to test for completion. If not
            // complete, sleep for 300 ms before polling again.
            TraceMsg(DateTime.Now, "Poll repeatedly until method is complete.");

            while (!asyncResult.IsCompleted)
            {
                TraceMsg(DateTime.Now, "Polling...");
                Thread.Sleep(300);
            }

            // Obtain the completion data for the asynchronous method.
            DateTime completion = DateTime.MinValue;

            try
            {
                completion = longRunningMethod.EndInvoke(asyncResult);
            }
            catch
            {
                // Catch and handle those exceptions you would if calling
                // LongRunningMethod directly.
            }

            // Display completion information.
            TraceMsg(completion, "Polling example complete.");
        }
// This method executes LongRunningMethod asynchronously and then
        // uses a WaitHandle to wait efficiently until LongRunningMethod
        // completes. Use of a timeout allows the method to break out of
        // waiting in order to update the user interface or fail if the
        // asynchronous method is taking too long.
        public static void WaitingExample()
        {
            Console.WriteLine(Environment.NewLine +
                "*** Running Waiting Example ***");

            // Invoke LongRunningMethod asynchronously. Pass null for both the
            // callback delegate and the asynchronous state object.
            AsyncExampleDelegate longRunningMethod = LongRunningMethod;

            IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000,
                "Waiting", null, null);

            // Wait for the asynchronous method to complete. Time out after
            // 300 ms and display status to the console before continuing to
            // wait.
            TraceMsg(DateTime.Now, "Waiting until method is complete...");

            while (!asyncResult.AsyncWaitHandle.WaitOne(300, false))
            {
                TraceMsg(DateTime.Now, "Wait timeout...");
            }

            // Obtain the completion data for the asynchronous method.
            DateTime completion = DateTime.MinValue;

            try
            {
                completion = longRunningMethod.EndInvoke(asyncResult);
            }
            catch
            {
                // Catch and handle those exceptions you would if calling
                // LongRunningMethod directly.
            }

            // Display completion information.
            TraceMsg(completion, "Waiting example complete.");
        }

        // This method executes LongRunningMethod asynchronously multiple
        // times and then uses an array of WaitHandle objects to wait
        // efficiently until all of the methods are complete. Use of
        // a timeout allows the method to break out of waiting in order
        // to update the user interface or fail if the asynchronous
        // method is taking too long.
public static void WaitAllExample()
        {
            Console.WriteLine(Environment.NewLine +
                "*** Running WaitAll Example ***");

            // An ArrayList to hold the IAsyncResult instances for each of the
            // asynchronous methods started.
            ArrayList asyncResults = new ArrayList(3);

            // Invoke three LongRunningMethods asynchronously. Pass null for
            // both the callback delegate and the asynchronous state object.
            // Add the IAsyncResult instance for each method to the ArrayList.
            AsyncExampleDelegate longRunningMethod = LongRunningMethod;

            asyncResults.Add(longRunningMethod.BeginInvoke(3000,
                "WaitAll 1", null, null));

            asyncResults.Add(longRunningMethod.BeginInvoke(2500,
                "WaitAll 2", null, null));

            asyncResults.Add(longRunningMethod.BeginInvoke(1500,
                "WaitAll 3", null, null));

            // Create an array of WaitHandle objects that will be used to wait
            // for the completion of all the asynchronous methods.
            WaitHandle[] waitHandles = new WaitHandle[3];

            for (int count = 0; count < 3; count++)
            {
                waitHandles[count] =
                    ((IAsyncResult)asyncResults[count]).AsyncWaitHandle;
            }

            // Wait for all three asynchronous method to complete. Time out
            // after 300 ms and display status to the console before continuing
            // to wait.
            TraceMsg(DateTime.Now, "Waiting until all 3 methods are complete...");

            while (!WaitHandle.WaitAll(waitHandles, 300, false))
            {
                TraceMsg(DateTime.Now, "WaitAll timeout...");
            }

            // Inspect the completion data for each method, and determine the
            // time at which the final method completed.
            DateTime completion = DateTime.MinValue;
foreach (IAsyncResult result in asyncResults)
            {
                try
                {
                    DateTime time = longRunningMethod.EndInvoke(result);
                    if (time > completion) completion = time;
                }
                catch
                {
                    // Catch and handle those exceptions you would if calling
                    // LongRunningMethod directly.
                }
            }

            // Display completion information.
            TraceMsg(completion, "WaitAll example complete.");
        }

        // This method executes LongRunningMethod asynchronously and passes
        // an AsyncCallback delegate instance. The referenced CallbackHandler
        // method is called automatically when the asynchronous method
        // completes, leaving this method free to continue processing.
        public static void CallbackExample()
        {
            Console.WriteLine(Environment.NewLine +
                "*** Running Callback Example ***");

            // Invoke LongRunningMethod asynchronously. Pass an AsyncCallback
            // delegate instance referencing the CallbackHandler method that
            // will be called automatically when the asynchronous method
            // completes. Pass a reference to the AsyncExampleDelegate delegate
            // instance as asynchronous state; otherwise, the callback method
            // has no access to the delegate instance in order to call
            // EndInvoke.
            AsyncExampleDelegate longRunningMethod = LongRunningMethod;

            IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000,
                "Callback", CallbackHandler, longRunningMethod);

            // Continue with other processing.
            for (int count = 0; count < 15; count++)
            {
                TraceMsg(DateTime.Now, "Continue processing...");
                Thread.Sleep(200);
            }
        }

        // A method to handle asynchronous completion using callbacks.
        public static void CallbackHandler(IAsyncResult result)
        {
// Extract the reference to the AsyncExampleDelegate instance
            // from the IAsyncResult instance. This allows you to obtain the
            // completion data.
            AsyncExampleDelegate longRunningMethod =
                (AsyncExampleDelegate)result.AsyncState;

            // Obtain the completion data for the asynchronous method.
            DateTime completion = DateTime.MinValue;

            try
            {
                completion = longRunningMethod.EndInvoke(result);
            }
            catch
            {
                // Catch and handle those exceptions you would if calling
                // LongRunningMethod directly.
            }

            // Display completion information.
            TraceMsg(completion, "Callback example complete.");
        }

        public static void Main()
        {
            // Demonstrate the various approaches to asynchronous method completion.
            BlockingExample();
            PollingExample();
            WaitingExample();
            WaitAllExample();
            CallbackExample();

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Execute a Method Periodically

Problem

You need to execute a method in a separate thread periodically.

Solution

Declare a method containing the code you want to execute periodically. The method's signature must match that defined by the System.Threading.TimerCallback delegate; in other words, it must return void and take a single object argument. Create a System.Threading.Timer object and pass it the method you want to execute along with a state object that the timer will pass to your method when the timer expires. The runtime will wait until the timer expires and then call your method using a thread from the thread pool.

Tip

If you are implementing a timer in a Windows Forms application, you should consider using the System.Windows.Forms.Timer, which also provides additional support in Visual Studio that allows you to drag the timer from your toolbox onto your application. For server-based applications where you want to signal multiple listeners each time the timer fires, consider using the System.Timers.Timer class, which notifies listeners using events.

How It Works

It is often useful to execute a method at regular intervals. For example, you might need to clean a data cache every 20 minutes. The Timer class makes the periodic execution of methods straightforward, allowing you to execute a method referenced by a TimerCallback delegate at specified intervals. The referenced method executes in the context of a thread from the thread pool. (See recipe 4-1 for notes on the appropriate use of thread-pool threads.)

When you create a Timer object, you specify two time intervals. The first value specifies the millisecond delay until the Timer first executes your method. Specify 0 to execute the method immediately, and specify System.Threading.Timeout.Infinite to create the Timer in an unstarted state. The second value specifies the interval in milliseconds; then the Timer will repeatedly call your method following the initial execution. If you specify a value of 0 or Timeout.Infinite, the Timer will execute the method only once (as long as the initial delay is not Timeout.Infinite). You can specify the time intervals as int, long, uint, or System.TimeSpan values.

Once you have created a Timer object, you can modify the intervals used by the timer using the Change method, but you cannot change the method that is called. When you have finished with a Timer object, you should call its Dispose method to free system resources held by the timer. Disposing of the Timer object cancels any method that is scheduled for execution.

The Code

The TimerExample class shown next demonstrates how to use a Timer object to call a method named TimerHandler. Initially, the Timer object is configured to call TimerHandler after 2 seconds and then at 1-second intervals. The example allows you to enter a new millisecond interval in the console, which is applied using the Timer.Change method.

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_03
    {
        public static void Main()
        {
            // Create the state object that is passed to the TimerHandler
            // method when it is triggered. In this case, a message to display.
            string state = "Timer expired.";

            Console.WriteLine("{0} : Creating Timer.",
                DateTime.Now.ToString("HH:mm:ss.ffff"));

            // Create a timer that fires first after 2 seconds and then every
            // second. Use an anonymous method for the timer expiry handler.
            using (Timer timer =
                new Timer(delegate(object s)
                            {Console.WriteLine("{0} : {1}",
                             DateTime.Now.ToString("HH:mm:ss.ffff"),s);
                            }
                , state, 2000, 1000))
            {
                int period;

                // Read the new timer interval from the console until the
                // user enters 0 (zero). Invalid values use a default value
                // of 0, which will stop the example.
                do
                {
                    try
                    {
                        period = Int32.Parse(Console.ReadLine());
                    }
                    catch (FormatException)
                    {
                        period = 0;
                    }

                    // Change the timer to fire using the new interval starting
                    // immediately.
                    if (period > 0) timer.Change(0, period);
                } while (period > 0);
            }
// Wait to continue.
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Execute a Method at a Specific Time

Problem

You need to execute a method in a separate thread at a specific time.

Solution

Declare a method containing the code you want to execute. The method's signature must match that defined by the System.Threading.TimerCallback delegate; that is, it must return void and take a single object argument. Create a System.Threading.Timer object, and pass it the method you want to execute along with a state object that the timer will pass to your method when the timer expires. Calculate the time difference between the current time and the desired execution time, and configure the Timer object to fire once after this period of time.

How It Works

Executing a method at a particular time is often useful. For example, you might need to back up data at 1 a.m. daily. Although primarily used for calling methods at regular intervals, the Timer object also provides the flexibility to call a method at a specific time.

When you create a Timer object, you specify two time intervals. The first value specifies the millisecond delay until the Timer first executes your method. To execute the method at a specific time, you should set this value to the difference between the current time (System.DateTime.Now) and the desired execution time. The second value specifies the interval after which the Timer will repeatedly call your method following the initial execution. If you specify a value of 0, System.Threading.Timeout.Infinite, or TimeSpan(-1), the Timer object will execute the method only once. If you need the method to execute at a specific time every day, you can easily set this figure using TimeSpan.FromDays(1), which represents the number of milliseconds in 24 hours.

The Code

The following code demonstrates how to use a Timer object to execute a method at a specified time:

using System;
using System.Threading;
using System.Globalization;
namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_04
    {
        public static void Main(string[] args)
        {
            // Create a 30-second timespan.
            TimeSpan waitTime = new TimeSpan(0, 0, 30);

            // Create a Timer that fires once at the specified time. Specify
            // an interval of −1 to stop the timer executing the method
            // repeatedly. Use an anonymouse method for the timer expiry handler.
            new Timer(delegate(object s)
            {
                Console.WriteLine("Timer fired at {0}",
                DateTime.Now.ToString("HH:mm:ss.ffff"));
            }
                      , null, waitTime, new TimeSpan(-1));

            Console.WriteLine("Waiting for timer. Press Enter to terminate.");
            Console.ReadLine();
        }
    }
}

Execute a Method by Signaling a WaitHandle Object

Problem

You need to execute one or more methods automatically when an object derived from System.Threading.WaitHandle is signaled.

Solution

Declare a method containing the code you want to execute. The method's signature must match that defined by the System.Threading.WaitOrTimerCallback delegate. Using the static ThreadPool.RegisterWaitForSingleObject method, register the method to execute and the WaitHandle object that will trigger execution when signaled.

How It Works

You can use classes derived from the WaitHandle class to trigger the execution of a method. Using the RegisterWaitForSingleObject method of the ThreadPool class, you can register a WaitOrTimerCallback delegate instance for execution by a thread-pool thread when a specified WaitHandle-derived object enters a signaled state. You can configure the thread pool to execute the method only once or to automatically reregister the method for execution each time the WaitHandle is signaled. If the WaitHandle is already signaled when you call RegisterWaitForSingleObject, the method will execute immediately. The Unregister method of the System.Threading.RegisteredWaitHandle object returned by the RegisterWaitForSingleObject method is used to cancel a registered wait operation.

The class most commonly used as a trigger is AutoResetEvent, which automatically returns to an unsignaled state after it is signaled. However, you can also use the ManualResetEvent, Mutex, and Semaphore classes, which require you to change the signaled state manually.

The Code

The following example demonstrates how to use an AutoResetEvent to trigger the execution of a method named EventHandler. (The AutoResetEvent class is discussed further in recipe 4-8.)

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_05
    {
        // A method that is executed when the AutoResetEvent is signaled
        // or the wait operation times out.
        private static void EventHandler(object state, bool timedout)
        {
            // Display appropriate message to the console based on whether
            // the wait timed out or the AutoResetEvent was signaled.
            if (timedout)
            {
                Console.WriteLine("{0} : Wait timed out.",
                    DateTime.Now.ToString("HH:mm:ss.ffff"));
            }
            else
            {
                Console.WriteLine("{0} : {1}",
                    DateTime.Now.ToString("HH:mm:ss.ffff"), state);
            }
        }

        public static void Main()
        {
            // Create the new AutoResetEvent in an unsignaled state.
            AutoResetEvent autoEvent = new AutoResetEvent(false);

            // Create the state object that is passed to the event handler
            // method when it is triggered. In this case, a message to display.
            string state = "AutoResetEvent signaled.";

            // Register the EventHandler method to wait for the AutoResetEvent to
            // be signaled. Set a timeout of 10 seconds, and configure the wait
            // operation to reset after activation (last argument).
RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(
                autoEvent, EventHandler, state, 10000, false);

            Console.WriteLine("Press ENTER to signal the AutoResetEvent" +
                " or enter "Cancel" to unregister the wait operation.");

            while (Console.ReadLine().ToUpper() != "CANCEL")
            {
                // If "Cancel" has not been entered into the console, signal
                // the AutoResetEvent, which will cause the EventHandler
                // method to execute. The AutoResetEvent will automatically
                // revert to an unsignaled state.
                autoEvent.Set();
            }

            // Unregister the wait operation.
            Console.WriteLine("Unregistering wait operation.");
            handle.Unregister(null);

            // Wait to continue.
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Execute a Method Using a New Thread

Problem

You need to execute code in its own thread, and you want complete control over the thread's state and operation.

Solution

Declare a method containing the code you want to execute. The method's signature must match that defined by the System.Threading.ThreadStart or System.Threading.ParameterizedThreadStart delegate. Create a new System.Threading.Thread object and pass the method as an argument to its constructor. Call the Thread.Start method to start the execution of your method.

How It Works

For maximum control and flexibility when creating multithreaded applications, you need to take a direct role in creating and managing threads. This is the most complex approach to multithreaded programming, but it is the only way to overcome the restrictions and limitations inherent in the approaches using thread-pool threads, as discussed in the preceding recipes. The Thread class provides the mechanism through which you create and control threads. To create and start a new thread, follow this process:

  1. Define a method that matches the ThreadStart or ParameterizedThreadStart delegate. The ThreadStart delegate takes no arguments and returns void. This means you cannot easily pass data to your new thread. The ParameterizedThreadStart delegate also returns void but takes a single object as an argument, allowing you to pass data to the method you want to run. (The ParameterizedThreadStart delegate is a welcome addition to .NET 2.0.) The method you want to execute can be static or an instance method.

  2. Create a new Thread object and pass your method as an argument to the Thread constructor. The new thread has an initial state of Unstarted (a member of the System.Threading.ThreadState enumeration) and is a foreground thread by default. If you want to configure it to be a background thread, you need to set its IsBackground property to true.

  3. Call Start on the Thread object, which changes its state to ThreadState.Running and begins execution of your method. If you need to pass data to your method, include it as an argument to the Start call. If you call Start more than once, it will throw a System.Threading.ThreadStateException.

The Code

The following code demonstrates how to execute a method in a new thread and shows you how to pass data to the new thread:

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_06
    {

        // A utility method for displaying useful trace information to the
        // console along with details of the current thread.
        private static void TraceMsg(string msg)
        {
            Console.WriteLine("[{0,3}] - {1} : {2}",
                Thread.CurrentThread.ManagedThreadId,
                DateTime.Now.ToString("HH:mm:ss.ffff"), msg);
        }

        // A private class used to pass initialization data to a new thread.
        private class ThreadStartData
        {
            public ThreadStartData(int iterations, string message, int delay)
            {
                this.iterations = iterations;
this.message = message;
                this.delay = delay;
            }

            // Member variables hold initialization data for a new thread.
            private readonly int iterations;
            private readonly string message;
            private readonly int delay;

            // Properties provide read-only access to initialization data.
            public int Iterations { get { return iterations; } }
            public string Message { get { return message; } }
            public int Delay { get { return delay; } }
        }

        // Declare the method that will be executed in its own thread. The
        // method displays a message to the console a specified number of
        // times, sleeping between each message for a specified duration.
        private static void DisplayMessage(object config)
        {
            ThreadStartData data = config as ThreadStartData;

            if (data != null)
            {
                for (int count = 0; count < data.Iterations; count++)
                {
                    TraceMsg(data.Message);

                    // Sleep for the specified period.
                    Thread.Sleep(data.Delay);
                }
            }
            else
            {
                TraceMsg("Invalid thread configuration.");
            }
        }

        public static void Main()
        {
            // Create a new Thread object specifying DisplayMessage
            // as the method it will execute.
            Thread thread = new Thread(DisplayMessage);
            // Make this a foreground thread - this is the
            // default - call used for example purposes.
            thread.IsBackground = false;

            // Create a new ThreadStartData object to configure the thread.
            ThreadStartData config =
                new ThreadStartData(5, "A thread example.", 500);
            TraceMsg("Starting new thread.");
// Start the new thread and pass the ThreadStartData object
            // containing the initialization data.
            thread.Start(config);

            // Continue with other processing.
            for (int count = 0; count < 13; count++)
            {
                TraceMsg("Main thread continuing processing...");
                Thread.Sleep(200);
            }

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Synchronize the Execution of Multiple Threads Using a Monitor

Problem

You need to coordinate the activities of multiple threads within a single process to ensure the efficient use of shared resources or to ensure that several threads are not updating the same shared resource at the same time. (See recipe 4-9 for details of coordination between processes.)

Solution

Identify an appropriate object to use as a mechanism to control access to the shared resource/data. Use the static method Monitor.Enter to acquire a lock on the object, and use the static method Monitor.Exit to release the lock so another thread may acquire it.

How It Works

The greatest challenge in writing a multithreaded application is ensuring that the threads work in concert. This is commonly referred to as thread synchronization, and includes the following:

  • Ensuring that threads access shared objects and data correctly so that they do not cause corruption

  • Ensuring that threads execute only when they are meant to and cause minimum overhead when they are idle

The most commonly used synchronization mechanism is the System.Threading.Monitor class. The Monitor class allows a single thread to obtain an exclusive lock on an object by calling the static method Monitor.Enter. By acquiring an exclusive lock prior to accessing a shared resource or shared data, you ensure that only one thread can access the resource concurrently. Once the thread has finished with the resource, release the lock to allow another thread to access it. A block of code that enforces this behavior is often referred to as a critical section.

Note

Monitors are managed-code synchronization mechanisms that do not rely on any specific operating system primitives. This ensures that your code is portable should you want to run it on a non-Windows platform. This is in contrast to the synchronization mechanisms discussed in recipes 4-8, 4-9, and 4-10, which rely on Win32 operating system–based synchronization objects.

You can use any object to act as the lock; it is common to use the keyword this to obtain a lock on the current object, but it is better to use a separate object dedicated to the purpose of synchronization. The key point is that all threads attempting to access a shared resource must try to acquire the same lock. Other threads that attempt to acquire a lock using Monitor.Enter on the same object will block (enter a WaitSleepJoin state), and will be added to the lock's ready queue until the thread that owns the lock releases it by calling the static method Monitor.Exit. When the owning thread calls Exit, one of the threads from the ready queue acquires the lock. If the owner of a lock does not release it by calling Exit, all other threads will block indefinitely. Therefore, it is important to place the Exit call within a finally block to ensure that it is called even if an exception occurs. To ensure that threads do not wait indefinitely, you can specify a timeout value when you call Monitor.Enter.

Tip

Because Monitor is used so frequently in multithreaded applications, C# provides language-level support through the lock statement, which the compiler translates to the use of the Monitor class. A block of code encapsulated in a lock statement is equivalent to calling Monitor.Enter when entering the block and Monitor.Exit when exiting the block. In addition, the compiler automatically places the Monitor.Exit call in a finally block to ensure that the lock is released if an exception is thrown.

Using Monitor.Enter and Monitor.Exit is often all you will need to correctly synchronize access to a shared resource in a multithreaded application. However, when you are trying to coordinate the activation of a pool of threads to handle work items from a shared queue, Monitor.Enter and Monitor.Exit will not be sufficient. In this situation, you want a potentially large number of threads to wait efficiently until a work item becomes available without putting unnecessary load on the central processing unit (CPU). This is where you need the fine-grained synchronization control provided by the Monitor.Wait, Monitor.Pulse, and Monitor.PulseAll methods.

The thread that currently owns the lock can call Monitor.Wait, which will release the lock and place the calling thread on the lock's wait queue. Threads in a wait queue also have a state of WaitSleepJoin, and will continue to block until a thread that owns the lock calls either the Monitor.Pulse method or the Monitor.PulseAll method. Monitor.Pulse moves one of the waiting threads from the wait queue to the ready queue, and Monitor.PulseAll moves all threads. Once a thread has moved from the wait queue to the ready queue, it can acquire the lock the next time the lock is released. It is important to understand that threads on a lock's wait queue will not acquire a released lock; they will wait indefinitely until you call Monitor.Pulse or Monitor.PulseAll to move them to the ready queue.

So, in practice, when your pool threads are inactive, they sit on the wait queue. As a new work item arrives, a dispatcher obtains the lock and calls Monitor.Pulse, moving one worker thread to the ready queue where it will obtain the lock as soon as the dispatcher releases it. The worker thread takes the work item, releases the lock, and processes the work item. Once the worker thread has finished with the work item, it again obtains the lock in order to take the next work item, but if there is no work item to process, the thread calls Monitor.Wait and goes back to the wait queue.

The Code

The following example demonstrates how to synchronize access to a shared resource (the console) and the activation of waiting threads using the Monitor.Wait, Monitor.Pulse, and Monitor.PulseAll methods. The example starts three worker threads that take work items from a queue and processes them. When the user presses Enter the first two times, work items (strings in the example) are added to the work queue, and Monitor.Pulse is called to release one waiting thread for each work item. The third time the user presses Enter, Monitor.PulseAll is called, releasing all waiting threads and allowing them to terminate.

using System;
using System.Threading;
using System.Collections.Generic;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_07
    {
        // Declare an object for synchronization of access to the console.
        // A static object is used because you are using it in static methods.
        private static object consoleGate = new Object();

        // Declare a Queue to represent the work queue.
        private static Queue<string> workQueue = new Queue<string>();

        // Declare a flag to indicate to activated threads that they should
        // terminate and not process more work items.
        private static bool processWorkItems = true;

        // A utility method for displaying useful trace information to the
        // console along with details of the current thread.
        private static void TraceMsg(string msg)
        {
            lock (consoleGate)
            {
Console.WriteLine("[{0,3}/{1}] - {2} : {3}",
                    Thread.CurrentThread.ManagedThreadId,
                    Thread.CurrentThread.IsThreadPoolThread ? "pool" : "fore",
                    DateTime.Now.ToString("HH:mm:ss.ffff"), msg);
            }
        }

        // Declare the method that will be executed by each thread to process
        // items from the work queue.
        private static void ProcessWorkItems()
        {
            // A local variable to hold the work item taken from the work queue.
            string workItem = null;

            TraceMsg("Thread started, processing items from queue...");

            // Process items from the work queue until termination is signaled.
            while (processWorkItems)
            {
                // Obtain the lock on the work queue.
                Monitor.Enter(workQueue);

                try
                {
                    // Pop the next work item and process it, or wait if none
                    // is available.
                    if (workQueue.Count == 0)
                    {
                        TraceMsg("No work items, waiting...");

                        // Wait until Pulse is called on the workQueue object.
                        Monitor.Wait(workQueue);
                    }
                    else
                    {
                        // Obtain the next work item.
                        workItem = workQueue.Dequeue();
                    }
                }
                finally
                {
                    // Always release the lock.
                    Monitor.Exit(workQueue);
                }

                // Process the work item if one was obtained.
                if (workItem != null)
                {
// Obtain a lock on the console and display a series
                    // of messages.
                    lock (consoleGate)
                    {
                        for (int i = 0; i < 5; i++)
                        {
                            TraceMsg("Processing " + workItem);
                            Thread.Sleep(200);
                        }
                    }

                    // Reset the status of the local variable.
                    workItem = null;
                }
            }

            // This will be reached only if processWorkItems is false.
            TraceMsg("Terminating.");
        }

        public static void Main()
        {
            TraceMsg("Starting worker threads.");

            // Add an initial work item to the work queue.
            lock (workQueue)
            {
                workQueue.Enqueue("Work Item 1");
            }

            // Create and start three new worker threads running the
            // ProcessWorkItems method.
            for (int count = 0; count < 3; count++)
            {
                (new Thread(ProcessWorkItems)).Start();
            }

            Thread.Sleep(1500);

            // The first time the user presses Enter, add a work item and
            // activate a single thread to process it.
            TraceMsg("Press Enter to pulse one waiting thread.");
            Console.ReadLine();

            // Acquire a lock on the workQueue object.
            lock (workQueue)
            {
                // Add a work item.
                workQueue.Enqueue("Work Item 2.");
// Pulse one waiting thread.
                Monitor.Pulse(workQueue);
            }

            Thread.Sleep(2000);

            // The second time the user presses Enter, add three work items and
            // activate three threads to process them.
            TraceMsg("Press Enter to pulse three waiting threads.");
            Console.ReadLine();

            // Acquire a lock on the workQueue object.
            lock (workQueue)
            {
                // Add work items to the work queue, and activate worker threads.
                workQueue.Enqueue("Work Item 3.");
                Monitor.Pulse(workQueue);
                workQueue.Enqueue("Work Item 4.");
                Monitor.Pulse(workQueue);
                workQueue.Enqueue("Work Item 5.");
                Monitor.Pulse(workQueue);
            }

            Thread.Sleep(3500);

            // The third time the user presses Enter, signal the worker threads
            // to terminate and activate them all.
            TraceMsg("Press Enter to pulse all waiting threads.");
            Console.ReadLine();

            // Acquire a lock on the workQueue object.
            lock (workQueue)
            {
                // Signal that threads should terminate.
                processWorkItems = false;

                // Pulse all waiting threads.
                Monitor.PulseAll(workQueue);
            }

            Thread.Sleep(1000);

            // Wait to continue.
            TraceMsg("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Synchronize the Execution of Multiple Threads Using an Event

Problem

You need a mechanism to synchronize the execution of multiple threads in order to coordinate their activities or access to shared resources.

Solution

Use the EventWaitHandle, AutoResetEvent, and ManualResetEvent classes from the System.Threading namespace.

How It Works

The EventWaitHandle, AutoResetEvent, and ManualResetEvent classes provide similar functionality. EventWaitHandle is the base class from which the AutoResetEvent and ManualResetEvent classes are derived. (EventWaitHandle inherits from System.Threading.WaitHandle and allows you to create named events.) All three event classes allow you to synchronize multiple threads by manipulating the state of the event between two possible values: signaled and unsignaled.

Threads requiring synchronization call static or inherited methods of the WaitHandle abstract base class (summarized in Table 4-1) to test the state of one or more event objects. If the events are signaled when tested, the thread continues to operate unhindered. If the events are unsignaled, the thread enters a WaitSleepJoin state, blocking until one or more of the events become signaled or when a given timeout expires.

Table 4.1. WaitHandle Methods for Synchronizing Thread Execution

Method

Description

WaitOne

Causes the calling thread to enter a WaitSleepJoin state and wait for a specific WaitHandle-derived object to be signaled. You can also specify a timeout value. The WaitingExample method in recipe 4-2 demonstrates how to use the WaitOne method.

WaitAny

A static method that causes the calling thread to enter a WaitSleepJoin state and wait for any one of the objects in a WaitHandle array to be signaled. You can also specify a timeout value.

WaitAll

A static method that causes the calling thread to enter a WaitSleepJoin state and wait for all the WaitHandle objects in a WaitHandle array to be signaled. You can also specify a timeout value. The WaitAllExample method in recipe 4-2 demonstrates how to use the WaitAll method.

SignalAndWait

A static method that causes the calling thread to signal a specified event object and then wait on a specified event object. The signal and wait operations are carried out as an atomic operation. You can also specify a timeout value. SignalAndWait is new to .NET 2.0.

The key differences between the three event classes are how they transition from a signaled to an unsignaled state, and their visibility. Both the AutoResetEvent and ManualResetEvent classes are local to the process in which they are declared. To signal an AutoResetEvent class, call its Set method, which will release only one thread that is waiting on the event. The AutoResetEvent class will then automatically return to an unsignaled state. The code in recipe 4-4 demonstrates how to use an AutoResetEvent class.

The ManualResetEvent class must be manually switched back and forth between signaled and unsignaled states using its Set and Reset methods. Calling Set on a ManualResetEvent class will set it to a signaled state, releasing all threads that are waiting on the event. Only by calling Reset does the ManualResetEvent class become unsignaled.

You can configure the EventWaitHandle class to operate in a manual or automatic reset mode, making it possible to act like either the AutoResetEvent class or the ManualResetEvent class. When you create the EventWaitHandle, you pass a value of the System.Threading.EventResetMode enumeration to configure the mode in which the EventWaitHandle will function; the two possible values are AutoReset and ManualReset. The unique benefit of the EventWaitHandle class is that it is not constrained to the local process. When you create an EventWaitHandle class, you can associate a name with it that makes it accessible to other processes, including unmanaged Win32 code. This allows you to synchronize the activities of threads across process and application domain boundaries and synchronize access to resources that are shared by multiple processes. To obtain a reference to an existing named EventWaitHandle, call the static method EventWaitHandle.OpenExisting and specify the name of the event.

The Code

The following example demonstrates how to use a named EventWaitHandle in manual mode that is initially signaled. A thread is spawned that waits on the event and then displays a message to the console—repeating the process every 2 seconds. When you press Enter, you toggle the event between a signaled and a unsignaled state. This example uses the Thread.Join instance method, which we describe in recipe 4-12.

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_08
    {
        // Boolean to signal that the second thread should terminate.
        static bool terminate = false;

        // A utility method for displaying useful trace information to the
        // console along with details of the current thread.
private static void TraceMsg(string msg)
        {
            Console.WriteLine("[{0,3}] - {1} : {2}",
                Thread.CurrentThread.ManagedThreadId,
                DateTime.Now.ToString("HH:mm:ss.ffff"), msg);
        }

        // Declare the method that will be executed on the separate thread.
        // The method waits on the EventWaitHandle before displaying a message
        // to the console and then waits two seconds and loops.
        private static void DisplayMessage()
        {
            // Obtain a handle to the EventWaitHandle with the name "EventExample".
            EventWaitHandle eventHandle =
                EventWaitHandle.OpenExisting("EventExample");

            TraceMsg("DisplayMessage Started.");

            while (!terminate)
            {
                // Wait on the EventWaitHandle, time out after 2 seconds. WaitOne
                // returns true if the event is signaled; otherwise, false. The
                // first time through, the message will be displayed immediately
                // because the EventWaitHandle was created in a signaled state.
                if (eventHandle.WaitOne(2000, true))
                {
                    TraceMsg("EventWaitHandle In Signaled State.");
                }
                else
                {
                    TraceMsg("WaitOne Timed Out -- " +
                        "EventWaitHandle In Unsignaled State.");
                }
                Thread.Sleep(2000);
            }

            TraceMsg("Thread Terminating.");
        }

        public static void Main()
        {
            // Create a new EventWaitHandle with an initial signaled state, in
            // manual mode, with the name "EventExample".
            using (EventWaitHandle eventWaitHandle =
                new EventWaitHandle(true, EventResetMode.ManualReset,
                "EventExample"))
            {
// Create and start a new thread running the DisplayMesssage
                // method.
                TraceMsg("Starting DisplayMessageThread.");
                Thread trd = new Thread(DisplayMessage);
                trd.Start();

                // Allow the EventWaitHandle to be toggled between a signaled and
                // unsignaled state up to three times before ending.
                for (int count = 0; count < 3; count++)
                {
                    // Wait for Enter to be pressed.
                    Console.ReadLine();

                    // You need to toggle the event. The only way to know the
                    // current state is to wait on it with a 0 (zero) timeout
                    // and test the result.
                    if (eventWaitHandle.WaitOne(0, true))
                    {
                        TraceMsg("Switching Event To UnSignaled State.");

                        // Event is signaled, so unsignal it.
                        eventWaitHandle.Reset();
                    }
                    else
                    {
                        TraceMsg("Switching Event To Signaled State.");

                        // Event is unsignaled, so signal it.
                        eventWaitHandle.Set();
                    }
                }

                // Terminate the DisplayMessage thread, and wait for it to
                // complete before disposing of the EventWaitHandle.
                terminate = true;
                eventWaitHandle.Set();
                trd.Join(5000);
            }

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Synchronize the Execution of Multiple Threads Using a Mutex

Problem

You need to coordinate the activities of multiple threads (possibly across process boundaries) to ensure the efficient use of shared resources or to ensure that several threads are not updating the same shared resource at the same time.

Solution

Use the System.Threading.Mutex class.

How It Works

The Mutex has a similar purpose to the Monitor discussed in recipe 4-7—it provides a means to ensure that only a single thread has access to a shared resource or section of code at any given time. However, unlike the Monitor, which is implemented fully within managed code, the Mutex is a wrapper around an operating system synchronization object. This, and because Mutexes can be given names, means you can use a Mutex to synchronize the activities of threads across process boundaries, even with threads running in unmanaged Win32 code.

Like the EventWaitHandle, AutoResetEvent, and ManualResetEvent classes discussed in recipe 4-8, the Mutex is derived from System.Threading.WaitHandle and enables thread synchronization in a similar fashion. A Mutex is in either a signaled state or an unsignaled state. A thread acquires ownership of the Mutex at construction or by using one of the methods listed in Table 4-1. If a thread has ownership of the Mutex, the Mutex is unsignaled, meaning other threads will block if they try to acquire ownership. Ownership of the Mutex is released by the owning thread calling the Mutex.ReleaseMutex method, which signals the Mutex and allows another thread to acquire ownership. A thread may acquire ownership of a Mutex any number of times without problems, but it must release the Mutex an equal number of times to free it and make it available for another thread to acquire. If the thread with ownership of a Mutex terminates normally, the Mutex becomes signaled, allowing another thread to acquire ownership.

The Code

The following example demonstrates how to use a named Mutex to limit access to a shared resource (the console) to a single thread at any given time:

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_09
    {
// Boolean to signal that the second thread should terminate.
        static bool terminate = false;

        // A utility method for displaying useful trace information to the
        // console along with details of the current thread.
        private static void TraceMsg(string msg)
        {
            Console.WriteLine("[{0,3}] - {1} : {2}",
                Thread.CurrentThread.ManagedThreadId,
                DateTime.Now.ToString("HH:mm:ss.ffff"), msg);
        }

        // Declare the method that will be executed on the separate thread.
        // In a loop the method waits to obtain a Mutex before displaying a
        // message to the console and then waits 1 second before releasing the
        // Mutex.
        private static void DisplayMessage()
        {
            // Obtain a handle to the Mutex with the name "MutexExample".
            // Do not attempt to take ownership immediately.
            using (Mutex mutex = new Mutex(false, "MutexExample"))
            {
                TraceMsg("Thread started.");

                while (!terminate)
                {
                    // Wait on the Mutex.
                    mutex.WaitOne();

                    TraceMsg("Thread owns the Mutex.");

                    Thread.Sleep(1000);

                    TraceMsg("Thread releasing the Mutex.");

                    // Release the Mutex.
                    mutex.ReleaseMutex();

                    // Sleep a little to give another thread a good chance of
                    // acquiring the Mutex.
                    Thread.Sleep(100);
                }

                TraceMsg("Thread terminating.");
            }
        }
public static void Main()
        {
            // Create a new Mutex with the name "MutexExample".
            using (Mutex mutex = new Mutex(false, "MutexExample"))
            {
                TraceMsg("Starting threads -- press Enter to terminate.");

                // Create and start three new threads running the
                // DisplayMesssage method.
                Thread trd1 = new Thread(DisplayMessage);
                Thread trd2 = new Thread(DisplayMessage);
                Thread trd3 = new Thread(DisplayMessage);
                trd1.Start();
                trd2.Start();
                trd3.Start();

                // Wait for Enter to be pressed.
                Console.ReadLine();

                // Terminate the DisplayMessage threads, and wait for them to
                // complete before disposing of the Mutex.
                terminate = true;
                trd1.Join(5000);
                trd2.Join(5000);
                trd3.Join(5000);
            }

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Note

Recipe 4-17 demonstrates how to use a named Mutex as a means to ensure that only a single instance of an application can be started at any given time.

Synchronize the Execution of Multiple Threads Using a Semaphore

Problem

You need to control the number of threads that can access a shared resource or section of code concurrently.

Solution

Use the System.Threading.Semaphore class.

How It Works

The Semaphore is another synchronization class derived from the System.Threading.WaitHandle class and will be familiar to those with Win32 programming experience. The purpose of the Semaphore is to allow a specified maximum number of threads to access a shared resource or section of code concurrently.

As with the other synchronization classes derived from WaitHandle (discussed in recipe 4-8 and recipe 4-9), a Semaphore is either in a signaled state or an unsignaled state. Threads wait for the Semaphore to become signaled using the methods described in Table 4-1. The Semaphore maintains a count of the active threads it has allowed through and automatically switches to an unsignaled state once the maximum number of threads is reached. To release the Semaphore and allow other waiting threads the opportunity to act, a thread calls the Release method on the Semaphore object. A thread may acquire ownership of the Semaphore more than once, reducing the maximum number of threads that can be active concurrently, and must call Release the same number of times to fully release it.

The Code

The following example demonstrates how to use a named Semaphore to limit access to a shared resource (the console) to two threads at any given time. The code is similar to that used in recipe 4-9 but substitutes a Semaphore for the Mutex.

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_10
    {
        // Boolean to signal that the second thread should terminate.
        static bool terminate = false;
// A utility method for displaying useful trace information to the
        // console along with details of the current thread.
        private static void TraceMsg(string msg)
        {
            Console.WriteLine("[{0,3}] - {1} : {2}",
                Thread.CurrentThread.ManagedThreadId,
                DateTime.Now.ToString("HH:mm:ss.ffff"), msg);
        }

        // Declare the method that will be executed on the separate thread.
        // In a loop the method waits to obtain a Semaphore before displaying a
        // message to the console and then waits 1 second before releasing the
        // Semaphore.
        private static void DisplayMessage()
        {
            // Obtain a handle to the Semaphore with the name "SemaphoreExample".
            using (Semaphore sem = Semaphore.OpenExisting("SemaphoreExample"))
            {
                TraceMsg("Thread started.");

                while (!terminate)
                {
                    // Wait on the Semaphore.
                    sem.WaitOne();

                    TraceMsg("Thread owns the Semaphore.");

                    Thread.Sleep(1000);

                    TraceMsg("Thread releasing the Semaphore.");

                    // Release the Semaphore.
                    sem.Release();

                    // Sleep a little to give another thread a good chance of
                    // acquiring the Semaphore.
                    Thread.Sleep(100);
                }

                TraceMsg("Thread terminating.");
            }
        }

        public static void Main()
        {
            // Create a new Semaphore with the name "SemaphoreExample". The
            // Semaphore can be owned by up to two threads at the same time.
            using (Semaphore sem = new Semaphore(2,2,"SemaphoreExample"))
            {
                TraceMsg("Starting threads -- press Enter to terminate.");
// Create and start three new threads running the
                // DisplayMesssage method.
                Thread trd1 = new Thread(DisplayMessage);
                Thread trd2 = new Thread(DisplayMessage);
                Thread trd3 = new Thread(DisplayMessage);
                trd1.Start();
                trd2.Start();
                trd3.Start();

                // Wait for Enter to be pressed.
                Console.ReadLine();

                // Terminate the DisplayMessage threads and wait for them to
                // complete before disposing of the Semaphore.
                terminate = true;
                trd1.Join(5000);
                trd2.Join(5000);
                trd3.Join(5000);
            }

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Synchronize Access to a Shared Data Value

Problem

You need to ensure operations on a numeric data value are executed atomically so that multiple threads accessing the value do not cause errors or corruption.

Solution

Use the static members of the System.Threading.Interlocked class.

How It Works

The Interlocked class contains several static methods that perform some simple arithmetic and comparison operations on a variety of data types and ensure the operations are carried out atomically. Table 4-2 summarizes the methods and the data types on which they can be used. Note that the methods use the ref keyword on their arguments to allow the method to update the value of the actual value type variable passed in. If the operations you want to perform are not supported by the Interlocked class, you will need to implement your own synchronization using the other approaches described in this chapter.

Warning

The atomicity of 64-bit interlocked operations on 32-bit platforms are guaranteed only when the data value is accessed through the Interlocked class—i.e., that the variable is not accessed directly.

Table 4.2. Interlocked Methods for Synchronizing Data Access

Method

Description

Add

Adds two int or long values and sets the value of the first argument to the sum of the two values.

CompareExchange

Compares two values; if they are the same, sets the first argument to a specified value. This method has overloads to support the comparison and exchange of int, long, float, double, object, and System.IntPtr.

Decrement

Decrements an int or long value.

Exchange

Sets the value of a variable to a specified value. This method has overloads to support the exchange of int, long, float, double, object, and System.IntPtr.

Increment

Increments an int or a long value.

The Code

The following simple example demonstrates how to use the methods of the Interlocked class. The example does not demonstrate Interlocked in the context of a multithreaded program and is provided only to clarify the syntax and effect of the various methods.

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_11
    {
        public static void Main()
        {
            int firstInt = 2500;
            int secondInt = 8000;
Console.WriteLine("firstInt initial value = {0}", firstInt);
            Console.WriteLine("secondInt initial value = {0}", secondInt);

            // Decrement firstInt in a thread-safe manner.
            // The thread-safe equivalent of firstInt = firstInt - 1.
            Interlocked.Decrement(ref firstInt);

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("firstInt after decrement = {0}", firstInt);

            // Increment secondInt in a thread-safe manner.
            // The thread-safe equivalent of secondInt = secondInt + 1.
            Interlocked.Increment(ref secondInt);

            Console.WriteLine("secondInt after increment = {0}", secondInt);

            // Add the firstInt and secondInt values, and store the result in
            // firstInt.
            // The thread-safe equivalent of firstInt = firstInt + secondInt.
            Interlocked.Add(ref firstInt, secondInt);

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("firstInt after Add = {0}", firstInt);
            Console.WriteLine("secondInt after Add = {0}", secondInt);

            // Exchange the value of firstInt with secondInt.
            // The thread-safe equivalenet of secondInt = firstInt.
            Interlocked.Exchange(ref secondInt, firstInt);

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("firstInt after Exchange = {0}", firstInt);
            Console.WriteLine("secondInt after Exchange = {0}", secondInt);

            // Compare firstInt with secondInt, and if they are equal, set
            // firstInt to 5000.
            // The thread-safe equivalenet of:
            //     if (firstInt == secondInt) firstInt = 5000.
            Interlocked.CompareExchange(ref firstInt, 5000, secondInt);

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("firstInt after CompareExchange = {0}", firstInt);
            Console.WriteLine("secondInt after CompareExchange = {0}", secondInt);

            // Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Know When a Thread Finishes

Problem

You need to know when a thread has finished.

Solution

Use the IsAlive property or the Join method of the Thread class.

How It Works

The easiest way to test whether a thread has finished executing is to test the Thread.IsAlive property. The IsAlive property returns true if the thread has been started but has not terminated or been aborted. The IsAlive property provides a simple test to see whether a thread has finished executing, but commonly you will need one thread to wait for another thread to complete its processing. Instead of testing IsAlive in a loop, which is inefficient, you can use the Thread.Join method.

Join causes the calling thread to block until the referenced thread terminates, at which point the calling thread will continue. You can optionally specify an int or a TimeSpan value that specifies the time, after which the Join operation will time out and execution of the calling thread will resume. If you specify a timeout value, Join returns true if the thread terminated and false if Join timed out.

The Code

The following example executes a second thread and then calls Join (with a timeout of 2 seconds) to wait for the second thread to terminate. Because the second thread takes about 5 seconds to execute, the Join method will always time out, and the example will display a message to the console. The example then calls Join again without a timeout and blocks until the second thread terminates.

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_12
    {
        private static void DisplayMessage()
        {
            // Display a message to the console five times.
            for (int count = 0; count < 5; count++)
            {
                Console.WriteLine("{0} : DisplayMessage thread",
                    DateTime.Now.ToString("HH:mm:ss.ffff"));
// Sleep for 1 second.
                Thread.Sleep(1000);
            }
        }

        public static void Main()
        {
            // Create a new Thread to run the DisplayMessage method.
            Thread thread = new Thread(DisplayMessage);

            Console.WriteLine("{0} : Starting DisplayMessage thread.",
                DateTime.Now.ToString("HH:mm:ss.ffff"));

            // Start the DisplayMessage thread.
            thread.Start();

            // Block until the DisplayMessage thread finishes, or time out after
            // 2 seconds.
            if (!thread.Join(2000))
            {
                Console.WriteLine("{0} : Join timed out !!",
                    DateTime.Now.ToString("HH:mm:ss.ffff"));
            }

            // Print out the thread status.
            Console.WriteLine("Thread alive: {0}", thread.IsAlive);

            // Block again until the DisplayMessage thread finishes with no timeout.
            thread.Join();

            // Print out the thread status.
            Console.WriteLine("Thread alive: {0}", thread.IsAlive);

            // Wait to continue.
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Terminate the Execution of a Thread

Problem

You need to terminate an executing thread without waiting for it to finish on its own.

Solution

Call the Abort method of the Thread object you want to terminate.

How It Works

It is better to write your code so that you can signal to a thread that it should shut down and allow it to terminate naturally. Recipes 4-7, 4-8, and 4-9 demonstrate this technique (using a Boolean flag). However, sometimes you will want a more direct method of terminating an active thread.

Calling Abort on an active Thread object terminates the thread by throwing a System.Threading.ThreadAbortException in the code that the thread is running. You can pass an object as an argument to the Abort method, which is accessible to the aborted thread through the ExceptionState property of the ThreadAbortException. When called, Abort returns immediately, but the runtime determines exactly when the exception is thrown, so you cannot assume the thread has terminated by the Abort returns. You should use the techniques described in recipe 4-12 if you need to determine when the aborted thread is actually done.

The aborted thread's code can catch the ThreadAbortException to perform cleanup, but the runtime will automatically throw the exception again when exiting the catch block to ensure that the thread terminates. So, you should not write code after the catch block: it will never execute. However, calling the static Thread.ResetAbort in the catch block will cancel the abort request and allow the thread to continue executing. Once you abort a thread, you cannot restart it by calling Thread.Start.

The Code

The following example creates a new thread that continues to display messages to the console until you press Enter, at which point the thread is terminated by a call to Thread.Abort:

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_13
    {
        private static void DisplayMessage()
        {
            try
            {
                while (true)
                {
                    // Display a message to the console.
                    Console.WriteLine("{0} : DisplayMessage thread active",
                        DateTime.Now.ToString("HH:mm:ss.ffff"));

                    // Sleep for 1 second.
                    Thread.Sleep(1000);
                }
            }
catch (ThreadAbortException ex)
            {
                // Display a message to the console.
                Console.WriteLine("{0} : DisplayMessage thread terminating - {1}",
                    DateTime.Now.ToString("HH:mm:ss.ffff"),
                    (string)ex.ExceptionState);

                // Call Thread.ResetAbort here to cancel the abort request.
            }

            // This code is never executed unless Thread.ResetAbort
            // is called in the previous catch block.
            Console.WriteLine("{0} : nothing is called after the catch block",
                DateTime.Now.ToString("HH:mm:ss.ffff"));
        }

        public static void Main()
        {
            // Create a new Thread to run the DisplayMessage method.
            Thread thread = new Thread(DisplayMessage);

            Console.WriteLine("{0} : Starting DisplayMessage thread" +
                " - press Enter to terminate.",
                DateTime.Now.ToString("HH:mm:ss.ffff"));

            // Start the DisplayMessage thread.
            thread.Start();

            // Wait until Enter is pressed and terminate the thread.
            Console.ReadLine();

            thread.Abort("User pressed Enter");

            // Block again until the DisplayMessage thread finishes.
            thread.Join();

            // Wait to continue.
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Create a Thread-Safe Collection Instance

Problem

You need multiple threads to be able to safely access the contents of a collection concurrently.

Solution

Use lock statements in your code to synchronize thread access to the collection, or to access the collection through a thread-safe wrapper.

How It Works

By default, the standard collection classes from the System.Collections, System.Collections.Specialized, and System.Collections.Generic namespaces will support multiple threads reading the collection's content concurrently. However, if more than one of these threads tries to modify the collection, you will almost certainly encounter problems. This is because the operating system can interrupt the actions of the thread while modifications to the collection have been only partially applied. This leaves the collection in an indeterminate state, which will almost certainly cause another thread accessing the collection to fail, return incorrect data, or corrupt the collection.

Note

.NET 4.0 introduces a set of efficient thread-safe collections in the System.Collections.Concurrent namespace that can be used. See Chapter 15 and the .NET Framework documentation for details.

The most commonly used collections from the System.Collections namespace implement a static method named Synchronized; this includes only the ArrayList, Hashtable, Queue, SortedList, and Stack classes. The Synchronized method takes a collection object of the appropriate type as an argument and returns an object that provides a synchronized wrapper around the specified collection object. The wrapper object is returned as the same type as the original collection, but all the methods and properties that read and write the collection ensure that only a single thread has access to the initial collection content concurrently. You can test whether a collection is thread-safe using the IsSynchronized property. One final note: Once you get the wrapper, you should neither access the initial collection nor create a new wrapper. In both cases, you will lose thread safety.

Collection classes such as HybridDictionary, ListDictionary, and StringCollection from the System.Collections.Specialized namespace do not implement a Synchronized method. To provide thread-safe access to instances of these classes, you must implement manual synchronization using the object returned by their SyncRoot property. This property and IsSynchronized are both defined by the ICollection interface that is implemented by all collection classes from System.Collections and System.Collections.Specialized (except BitVector32); you can therefore synchronize all your collections in a fine-grained way.

However, the new 2.0 classes in the System.Collections.Generic namespace provide no built-in synchronization mechanisms, leaving it to you to implement thread synchronization manually using the techniques discussed in this chapter.

Warning

Often you will have multiple collections and data elements that are related and need to be updated atomically. In these instances, you should not use the synchronization mechanisms provided by the individual collection classes. This approach will introduce synchronization problems into your code such as deadlocks and race conditions. You must decide what collections and other data elements need to be managed atomically and use the techniques described in this chapter to synchronize access to these elements as a unit.

The Code

The following code snippet shows how to create a thread-safe Hashtable instance:

// Create a standard Hashtable.
Hashtable hUnsync = new Hashtable();

// Create a synchronized wrapper.
Hashtable hSync = Hashtable.Synchronized(hUnsync);

The following code snippet shows how to create a thread-safe NameValueCollection. Notice that the NameValueCollection class derives from the NameObjectCollectionBase class, which uses an explicit interface implementation to implement the ICollection.SyncRoot property. As shown, you must cast the NameValueCollection to an ICollection instance before you can access the SyncRoot property. Casting is not necessary with other specialized collection classes such as HybridDictionary, ListDictionary, and StringCollection, which do not use explicit interface implementations to implement SyncRoot.

// Create a NameValueCollection.
NameValueCollection nvCollection = new NameValueCollection();

// Obtain a lock on the NameValueCollection before modification.
lock (((ICollection)nvCollection).SyncRoot) {

    // Modify the NameValueCollection...
}

Start a New Process

Problem

You need to execute an application in a new process.

Solution

Call one of the static Start method overloads of the System.Diagnostics.Process class. Specify the configuration details of the process you want to start as individual arguments to the Start method or in a System.Diagnostics.ProcessStartInfo object that you pass to the Start method.

How It Works

The Process class provides a managed representation of an operating system process and provides a simple mechanism through which you can execute both managed and unmanaged applications. The Process class implements five static overloads of the Start method, which you use to start a new process. All these methods return a Process object that represents the newly started process. Two of these overloads are methods that allow you to specify only the name and arguments to pass to the new process. For example, the following statements both execute Notepad in a new process:

// Execute notepad.exe with no command-line arguments.
Process.Start("notepad.exe");

// Execute notepad.exe passing the name of the file to open as a
// command-line argument.
Process.Start("notepad.exe", "SomeFile.txt");

Another two overloads extend these and allow you to specify the name of a Windows user who the process should run as. You must specify the username, password, and Windows domain. The password is specified as a System.Security.SecureString for added security. (See recipe 11-18 for more information about the SecureString class.) Here is an example:

System.Security.SecureString mySecureString = new System.Security.SecureString();

// Obtain a password and place in SecureString (see Recipe 11-18).

// Execute notepad.exe with no command-line arguments.
Process.Start("notepad.exe", "allen", mySecureString, "MyDomain");

// Execute notepad.exe passing the name of the file to open as a
// command-line argument.
Process.Start("notepad.exe", "SomeFile.txt", "allen", mySecureString, "MyDomain");

The remaining static overload requires you to create a ProcessStartInfo object configured with the details of the process you want to run; using the ProcessStartInfo object provides greater control over the behavior and configuration of the new process. Table 4-3 summarizes some of the commonly used properties of the ProcessStartInfo class.

Table 4.3. Properties of the ProcessStartInfo Class

Property

Description

Arguments

The command-line arguments to pass to the new process.

Domain

A string containing the Windows domain name to which the user belongs.

ErrorDialog

If Process.Start cannot start the specified process, it will throw a System.ComponentModel.Win32Exception. If ErrorDialog is true, Start displays an error dialog box to the user before throwing the exception.

FileName

The name of the application to start. You can also specify any type of file for which you have configured an application association. For example, you could specify a file with a .doc or an .xls extension, which would cause Microsoft Word or Microsoft Excel to run.

LoadUserProfile

A bool indicating whether the user's profile should be loaded from the registry when the new process is started.

Password

A SecureString containing the password of the user.

UserName

A string containing the name of the user to use when starting the process.

WindowStyle

A member of the System.Diagnostics.ProcessWindowStyle enumeration, which controls how the window is displayed. Valid values include Hidden, Maximized, Minimized, and Normal.

WorkingDirectory

The fully qualified name of the initial directory for the new process.

When finished with a Process object, you should dispose of it in order to release system resources—call Close, call Dispose, or create the Process object within the scope of a using statement.

Note

Disposing of a Process object does not affect the underlying system process, which will continue to run.

The Code

The following example uses Process to execute Notepad in a maximized window and open a file named C:Tempfile.txt. After creation, the example calls the Process.WaitForExit method, which blocks the calling thread until a process terminates or a specified timeout expires. This method returns true if the process ends before the timeout and returns false otherwise.

using System;
using System.Diagnostics;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_15
    {
        public static void Main()
        {
            // Create a ProcessStartInfo object and configure it with the
            // information required to run the new process.
            ProcessStartInfo startInfo = new ProcessStartInfo();

            startInfo.FileName = "notepad.exe";
            startInfo.Arguments = "file.txt";
            startInfo.WorkingDirectory = @"C:Temp";
            startInfo.WindowStyle = ProcessWindowStyle.Maximized;
            startInfo.ErrorDialog = true;

            // Declare a new Process object.
            Process process;

            try
            {
                // Start the new process.
                process = Process.Start(startInfo);

                // Wait for the new process to terminate before exiting.
                Console.WriteLine("Waiting 30 seconds for process to finish.");

                if (process.WaitForExit(30000))
                {
                    Console.WriteLine("Process terminated.");
                }
                else
                {
                    Console.WriteLine("Timed out waiting for process to end.");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Could not start process.");
                Console.WriteLine(ex);
            }
// Wait to continue.
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Terminate a Process

Problem

You need to terminate a process such as an application or a service.

Solution

Obtain a Process object representing the operating system process you want to terminate. For Windows-based applications, call Process.CloseMainWindow to send a close message to the application's main window. For Windows-based applications that ignore CloseMainWindow, or for non-Windows-based applications, call the Process.Kill method.

How It Works

If you start a new process from managed code using the Process class (discussed in recipe 4-15), you can terminate the process using the Process object that represents the new process. You can also obtain Process objects that refer to other currently running processes using the static methods of the Process class summarized in Table 4-4.

Table 4.4. Methods for Obtaining Process References

Method

Description

GetCurrentProcess

Returns a Process object representing the currently active process.

GetProcessById

Returns a Process object representing the process with the specified ID. This is the process ID (PID) you can get using Windows Task Manager.

GetProcesses

Returns an array of Process objects representing all currently active processes.

GetProcessesByName

Returns an array of Process objects representing all currently active processes with a specified friendly name. The friendly name is the name of the executable excluding file extension or path; for example, a friendly name could be notepad or calc.

Once you have a Process object representing the process you want to terminate, you need to call either the CloseMainWindow method or the Kill method. The CloseMainWindow method posts a WM_CLOSE message to a Windows-based application's main window. This method has the same effect as if the user had closed the main window using the system menu, and it gives the application the opportunity to perform its normal shutdown routine. CloseMainWindow will not terminate applications that do not have a main window or applications with a disabled main window—possibly because a modal dialog box is currently displayed. Under such circumstances, CloseMainWindow will return false.

CloseMainWindow returns true if the close message was successfully sent, but this does not guarantee that the process is actually terminated. For example, applications used to edit data will usually give the user the opportunity to save unsaved data if a close message is received. The user usually has the chance to cancel the close operation under such circumstances. This means CloseMainWindow will return true, but the application will still be running once the user cancels. You can use the Process.WaitForExit method to signal process termination and the Process.HasExited property to test whether a process has terminated. Alternatively, you can use the Kill method.

The Kill method simply terminates a process immediately; the user has no chance to stop the termination, and all unsaved data is lost. Kill is the only option for terminating Windows-based applications that do not respond to CloseMainWindow and for terminating non-Windows-based applications.

The Code

The following example starts a new instance of Notepad, waits 5 seconds, and then terminates the Notepad process. The example first tries to terminate the process using CloseMainWindow. If CloseMainWindow returns false, or the Notepad process is still running after CloseMainWindow is called, the example calls Kill and forces the Notepad process to terminate; you can force CloseMainWindow to return false by leaving the File Open dialog box open.

using System;
using System.Threading;
using System.Diagnostics;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_16
    {
        public static void Main()
        {
            // Create a new Process and run notepad.exe.
            using (Process process =
                Process.Start("notepad.exe",@"c:SomeFile.txt"))
            {
                // Wait for 5 seconds and terminate the notepad process.
                Console.WriteLine(
                    "Waiting 5 seconds before terminating notepad.exe.");
                Thread.Sleep(5000);

                // Terminate notepad process.
                Console.WriteLine("Terminating Notepad with CloseMainWindow.");
// Try to send a close message to the main window.
                if (!process.CloseMainWindow())
                {
                    // Close message did not get sent - Kill Notepad.
                    Console.WriteLine("CloseMainWindow returned false - " +
                        " terminating Notepad with Kill.");
                    process.Kill();
                }
                else
                {
                    // Close message sent successfully; wait for 2 seconds
                    // for termination confirmation before resorting to Kill.
                    if (!process.WaitForExit(2000))
                    {
                        Console.WriteLine("CloseMainWindow failed to" +
                            " terminate - terminating Notepad with Kill.");
                        process.Kill();
                    }
                }
            }

            // Wait to continue.
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Ensure That Only One Instance of an Application Can Execute Concurrently

Problem

You need to ensure that a user can have only one instance of an application running concurrently.

Solution

Create a named System.Threading.Mutex object, and have your application try to acquire ownership of it at startup.

How It Works

The Mutex provides a mechanism for synchronizing the execution of threads across process boundaries and in addition provides a convenient mechanism through which to ensure that only a single instance of an application is running concurrently. By trying to acquire ownership of a named Mutex at startup and exiting if the Mutex cannot be acquired, you can ensure that only one instance of your application is running.

The Code

This example uses a Mutex named MutexExample to ensure that only a single instance of the example can execute:

using System;
using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04
{
    class Recipe04_17
    {
        public static void Main()
        {
            // A Boolean that indicates whether this application has
            // initial ownership of the Mutex.
            bool ownsMutex;

            // Attempt to create and take ownership of a Mutex named
            // MutexExample.
            using (Mutex mutex =
                       new Mutex(true, "MutexExample", out ownsMutex))
            {
                // If the application owns the Mutex it can continue to execute;
                // otherwise, the application should exit.
                if (ownsMutex)
                {
                    Console.WriteLine("This application currently owns the" +
                        " mutex named MutexExample. Additional instances of" +
                        " this application will not run until you release" +
                        " the mutex by pressing Enter.");

                    Console.ReadLine();

                    // Release the mutex.
                    mutex.ReleaseMutex();
                }
                else
                {
                    Console.WriteLine("Another instance of this application " +
                        " already owns the mutex named MutexExample. This" +
                        " instance of the application will terminate.");
                }
            }
// Wait to continue.
            Console.WriteLine("Main method complete. Press Enter.");
            Console.ReadLine();
        }
    }
}

Note

If you do not construct the Mutex in a using statement and encapsulate the body of your application in the body of the using block as shown in this example, in long-running applications the garbage collector may dispose of the Mutex if it is not referenced after initial creation. This will result in releasing the Mutex and allow additional instances of the application to execute concurrently. In these circumstances, you should include the statement System.GC.KeepAlive(mutex) to ensure the Mutex is not garbage collected. Thanks to Michael A. Covington for highlighting this possibility.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.53.168