Chapter 7. Multithreading Gotchas

Multithreading is a powerful tool. It is also complex—I have enjoyed many a pizza at the office during lengthy debugging sessions. There are methods and facilities that work well only when used in a certain way. It is like tightrope walking; you can’t afford to slip to either side. Fully understand the capabilities, behavior, and limitations of the API before using it.

The Thread class provides a number of methods that are useful, a few that are not so useful, and some that are quite dangerous. Also, you have the options of creating a thread yourself, using one from the thread pool, or even using asynchronous features provided by the WinForms framework. Which one should you choose and why? What are the consequences of choosing one over the other? What happens if an exception is thrown from within a thread? How is this handled? Is it handled at all? What is the lifetime of a thread you create, and how can you control it? What happens when a thread is terminated? What about cleanup of resources? These are all questions you should ask before using the API.

In this chapter, I address these questions and provide answers that will steer you clear of the perils. I discuss general problems with threading, the thread pool, asynchronous calls using delegates, and threading problems related to Windows Forms [Griffiths03] and Web Services [Ballinger03, Ferrara02].

GOTCHA #48 The Thread classThread class

The Thread class provides a number of methods and properties to work with a specific thread. There are certain members in the Thread class, however, that you should not use, or use only with extreme caution. Here are some you should think twice about:

  • The IsAlive property tells you if the thread represented by the Thread instance is alive or not. The problem is that the thread may die right after you call this method, but before you make a decision based on that information.

  • The ThreadState property has the same drawbacks as IsAlive.

Now let’s look at some potentially dangerous methods:

  • The Suspend() method pauses, freezes, or suspends a thread of execution. But it is a blunt instrument. Any resources locked by the thread are held while it is blocked. This may easily result in a deadlock. Suspend() is not intended for normal day-to-day application programming. Do not use it, for example, to synchronize thread execution.

  • The Resume() method reactivates a suspended thread. For the same reasons mentioned above in connection with Suspend(), you should avoid it.

  • The ResetAbort() method cancels an Abort() request on a thread. This may surprise a programmer who has called Abort() and has a reasonable expectation that the thread will go away.

If you think you need any of the methods mentioned above, you should revisit your design (For good discussions of multithreading issues, refer to [Lea00].) The most effective way to communicate between threads is to use what I call bait. Set some fields to a certain (boolean) value and let the other thread check for that value to take a particular action or terminate. You might also consider other facilities, such as Wait(), Pulse(), Join(), and synchronizing on a wait handle.

IN A NUTSHELL

Certain methods and properties of the Thread class have unpredictable behavior. Avoid them, or use them with extreme caution.

GOTCHA #49 Foreground threads may prevent a program from terminating

The IsBackground property on a Thread indicates whether the thread is a background (daemon) thread. The CLR quits executing if only background threads are running. In other words, in normal execution the CLR does not terminate as long as there is at least one non-background thread running.

In your application, if you start a thread and leave it running in the foreground (which is the default), then exit the program (by leaving Main() or by clicking on the close box in a Windows application), the program continues to run as long as that thread runs. But if you set the thread to background, the program will terminate when you expect it to. Consider Example 7-1.

Example 7-1. Thread behavior

C# (IsBackground )

//Test.cs
using System;
using System.Threading;

namespace Background
{
    class Test
    {
        private static void Worker()
        {
            // Some activity
            Thread.Sleep(5000);
            Console.WriteLine("worker done at {0}",
                DateTime.Now.ToLongTimeString());
        }
        [STAThread]
        static void Main(string[] args)
        {
            Thread workerThread
                = new Thread(new ThreadStart(Worker));
            //workerThread.IsBackground = true;
            workerThread.Start();

            Console.WriteLine("Main done at {0}",
                DateTime.Now.ToLongTimeString());
        }
    }
}

VB.NET (IsBackground)

Imports System.Threading

Module Test

    Private Sub Worker()
        'Some activity
        Thread.Sleep(5000)

        Console.WriteLine("worker done at {0}", _
            DateTime.Now.ToLongTimeString())
    End Sub

    Sub Main()
        Dim workerThread As New Thread(AddressOf Worker)
        'workerThread.IsBackground = true
        workerThread.Start()

        Console.WriteLine("Main done at {0}", _
            DateTime.Now.ToLongTimeString())
    End Sub

End Module

In this example you create a Thread instance, assign it to execute the Worker() method, and start it. You print the message that Main() is done and leave the Main() method. When executed, the program will continue to run even after Main() completes. It terminates only after the Worker() method in the second thread completes, as shown in Figure 7-1.

Output from Example 7-1

Figure 7-1. Output from Example 7-1

If you uncomment the statement workerThread.IsBackground = true, the program terminates when it leaves the Main() method, as shown in Figure 7-2.

Output from Example 7-1 with IsBackground = true

Figure 7-2. Output from Example 7-1 with IsBackground = true

By default, threads are created in the foreground. Should you set IsBackground to true or not? Say you are performing a search in another thread. If the user quits the application, you may want the program to terminate. In this case, you need to set the thread to a background thread. On the other hand, if the task the other thread is performing is critical, and should be carried out no matter what other threads are running, then you don’t want to set it as background.

IN A NUTSHELL

Before starting a Thread, ask yourself if you should be setting the IsBackground property to true.

GOTCHA #50 Background threads don’t terminate gracefully

In Gotcha #49, "Foreground threads may prevent a program from terminating ,” you saw the advantage of setting a thread as background. Such a thread runs only as long as a foreground thread is running. But if your background thread uses critical resources, you want to make sure that it terminates gracefully.

The MSDN documentation says,

Once all foreground threads belonging to a process have terminated, the common language runtime ends the process by invoking Abort on any background threads that are still alive.

If you write a method that executes in the background, you should expect its thread to terminate when all foreground threads quit. Unfortunately, contrary to what Microsoft’s documentation might lead you to expect, the termination is not graceful. Let’s look at Example 7-2.

Example 7-2. Abrupt termination of a background thread

C# (BackgroundAbort)

using System;
using System.Threading;

namespace BackgroundThreadAndAbort
{
    class Test
    {
        private static void Worker()
        {
            Console.WriteLine(
                "Worker started... given chance to cleanup?");
            try
            {
                Thread.Sleep(5000);
            }
            catch(ThreadAbortException)
            {
                Console.WriteLine(
                    "Thread aborted exception received");
            }
        }

        [STAThread]
        static void Main(string[] args)
        {
            Thread workerThread
                = new Thread(new ThreadStart(Worker));
            workerThread.IsBackground = true;
            workerThread.Start();

            Thread.Sleep(2000);
            Console.WriteLine("Main done");
        }
    }
}

VB.NET (BackgroundAbort)

Imports System.Threading

Module Test

    Private Sub Worker()
        Console.WriteLine( _
         "Worker started... given chance to cleanup?")
        Try
            Thread.Sleep(5000)
        Catch ex As ThreadAbortException
               Console.WriteLine("Thread aborted exception received")
        End Try
    End Sub

    Public Sub Main()
        Dim workerThread As New Thread(AddressOf Worker)
        workerThread.IsBackground = True
        workerThread.Start()

        Thread.Sleep(2000)
        Console.WriteLine("Main done")
    End Sub

End Module

In this program, you start a background thread that executes the Worker() method. In that method you anticipate the ThreadAbortException. When Main() terminates, the background thread executing the Worker() method is terminated. However, the ThreadAbortException is not thrown on it. This is shown in the output in Figure 7-3. As a result, the background thread has no chance to clean up and exit gracefully. It just gets yanked.

Output from Example 7-2

Figure 7-3. Output from Example 7-2

IN A NUTSHELL

Remember that background threads are killed abruptly when the process terminates and do not have an opportunity to clean up gracefully.

GOTCHA #51 Interrupt () kicks in only when a thread is blocked

When you invoke the Interrupt() method on a thread, the CLR throws a ThreadInterruptedException in the thread’s context. However, this only happens when the thread is blocked—for instance, when it enters Sleep(), Join(), Wait(), or requests a lock. If the thread is busy executing, the exception is not received until the thread blocks. So if you call Interrupt() on a thread, the thread may not be interrupted for quite some time.

Furthermore, there is no guarantee that the thread will be interrupted at all. The thread may catch the ThreadInterruptedException and just ignore it. Consider Example 7-3.

Example 7-3. Interrupting a thread

C# (Interrupt)

using System;
using System.Threading;

namespace Interrupting
{
    class Test
    {
        public static void Worker()
        {
            try
            {
                Console.WriteLine("worker started at {0}",
                    DateTime.Now.ToLongTimeString());

                string str = null;
                for(int i = 0; i < 30000; i++)
                {
                    str += i.ToString();
                    // Simulating some activity
                }

                Thread.Sleep(1000);
            }
            catch(ThreadInterruptedException)
            {
                Console.WriteLine("Thread interrupted at {0}",
                    DateTime.Now.ToLongTimeString());
            }

            Console.WriteLine(
                "Continuing after Exception is caught");
        }

        [STAThread]
        static void Main(string[] args)
        {
            Thread workerThread = new Thread(
                new ThreadStart(Worker));
            //workerThread.IsBackground = true;
            workerThread.Start();
            Thread.Sleep(1000);

            Console.WriteLine("Interrupting worker at {0}",
                DateTime.Now.ToLongTimeString());

            workerThread.Interrupt();
        }
    }
}

VB.NET (Interrupt)

Imports System.Threading

Module Test
    Public Sub Worker()
        Try
            Dim str As String = Nothing

            Console.WriteLine("worker started at {0}", _
                DateTime.Now.ToLongTimeString())

            For i As Integer = 0 To 30000
                str += i.ToString()
                'Simulating some activity
            Next

            Thread.Sleep(1000)

        Catch ex As ThreadInterruptedException
            Console.WriteLine("Thread interrupted at {0}", _
                 DateTime.Now.ToLongTimeString())
        End Try

        Console.WriteLine( _
            "Continuing after Exception is caught")
    End Sub

    Public Sub Main()
        Dim workerThread As New Thread(AddressOf Worker)

        'workerThread.IsBackground = True

        workerThread.Start()
        Thread.Sleep(1000)

        Console.WriteLine("Interrupting worker at {0}", _
            DateTime.Now.ToLongTimeString())

        workerThread.Interrupt()
    End Sub

End Module

The Main() method creates a thread to execute the Worker() method. Worker() enters a busy computation cycle concatenating strings. Then Main() interrupts the thread. However, the ThreadInterruptedException does not take effect until the thread finishes its computation and arrives at Sleep(). When you execute the program, you get the output shown in Figure 7-4.

Output from Example 7-3

Figure 7-4. Output from Example 7-3

As you can see, even though the thread is interrupted one second after it was started, it did not receive the ThreadInterruptedException until fourteen seconds later.

IN A NUTSHELL

Remember that a thread receives a ThreadInterruptedException only when it blocks, and that it is free to ignore the request.

SEE ALSO

Gotcha #52, "ThreadAbortException—a hot potato" and Gotcha #54, "ResetAbort() may lead to surprises.”

GOTCHA #52 ThreadAbortException—a hot potato

When you call Thread.Abort() to abort a thread, the CLR throws a ThreadAbortException on it. This allows the thread to clean up its resources and terminate gracefully. Unlike the ThreadInterruptedException, the thread receives the ThreadAbortException instantaneously. This can be dangerous at times. If you are in the middle of a call, or doing some important processing, it does not wait for you to finish. Of course, you can handle the ThreadAbortException and stabilize your code. The ThreadAbortException, however, is a special kind of exception. When you leave the catch block, the CLR automatically throws it again, thus terminating the thread. Let’s take a look at this in Example 7-4.

Example 7-4. Behavior of Thread.Abort()

C# (Abort)

using System;
using System.Threading;

namespace ThreadAborting
{
    class Test
    {
        private static void Worker()
        {
            try
            {
                try
                {
                    Thread.Sleep(5000);
                }
                catch(ThreadAbortException)
                {
                    Console.WriteLine(
                        "ThreadAbortException caught");
                }

                Console.WriteLine(
                    "Let's leave the method now");
            }
            finally
            {
                Console.WriteLine("In the finally block");
            }
        }

        [STAThread]
        static void Main(string[] args)
        {
            Thread workerThread = new Thread(
                new ThreadStart(Worker));
            workerThread.Start();

            Thread.Sleep(1000);
            Console.WriteLine("Calling abort");
            workerThread.Abort();

            Thread.Sleep(1000);
            Console.WriteLine("Main done");
        }
    }
}

VB.NET (Abort)

Imports System.Threading

Module Test

    Private Sub Worker()
        Try
            Try
                Thread.Sleep(5000)
            Catch ex As ThreadAbortException
                Console.WriteLine("ThreadAbortException caught")
            End Try

            Console.WriteLine("Let's leave the method now")
        Finally
            Console.WriteLine("In the finally block")
        End Try
    End Sub

    Public Sub Main()
        Dim workerThread As New Thread(AddressOf Worker)
        workerThread.Start()

        Thread.Sleep(1000)
        Console.WriteLine("Calling abort")
        workerThread.Abort()

        Thread.Sleep(1000)
        Console.WriteLine("Main done")
    End Sub

End Module

In this example, Main()starts a thread to run the Worker() method. Then it invokes Abort() on that thread. The thread receives the ThreadAbortException right away. The exception is caught in the catch block. Note that there is no throw within the catch block; however, the exception is re-thrown automatically. As a result, instead of the "Let's leave the method now" message, the "In the finally block" message gets printed, as shown in the output in Figure 7-5.

Output from Example 7-4

Figure 7-5. Output from Example 7-4

The ThreadAbortException is a hot potato. The CLR throws it automatically from the catch block in order to terminate the thread (Want to catch it again? No problem, it’ll be re-thrown again). There is no guarantee that any statements in your method will be executed. However, all the finally blocks in the call stack are visited.

IN A NUTSHELL

Write code defensively with the expectation that the thread may be aborted. Understand the special nature of the ThreadAbortException.

GOTCHA #53 Environment.Exit() brings down the CLR

How do you exit from a thread? You do that by returning from its associated method. There is an Environment.Exit() method, but it is somewhat more drastic: if you call the Exit() method you terminate the process, no matter which thread you call it from. None of the application’s other threads gets a chance to clean up gracefully. Consider the code in Example 7-5.

Example 7-5. Behavior of Exit()

C# (KnowTheExit)

using System;
using System.Threading;

namespace Exit
{
    class Test
    {
        public static void Worker()
        {
            Console.WriteLine("worker thread started");
            try
            {
                Thread.Sleep(5000);
            }
            catch(ThreadInterruptedException)
            {
                Console.WriteLine("worker interrupted");
            }
            catch(ThreadAbortException)
            {
                Console.WriteLine("worker aborted");
            }
        }

        [STAThread]
        static void Main(string[] args)
        {
            Thread workerThread1 = new Thread(
                new ThreadStart(Worker));
            workerThread1.Start();
            Thread.Sleep(1000);

            Console.WriteLine("Interrupting worker1 at {0}",
                DateTime.Now.ToLongTimeString());

            workerThread1.Interrupt();

            Thread workerThread2 = new Thread(
                new ThreadStart(Worker));
            workerThread2.Start();
            Thread.Sleep(1000);

            Console.WriteLine("Calling Exit");

            Environment.Exit(0);
        }
    }
}

VB.NET (KnowTheExit)

Imports System.Threading

Module Test

    Public Sub Worker()
        Console.WriteLine("worker thread started")
        Try
            Thread.Sleep(5000)
        Catch ex As ThreadInterruptedException
            Console.WriteLine("worker interrupted")
        Catch ex As ThreadAbortException
            Console.WriteLine("worker aborted")
        End Try
    End Sub

    Public Sub Main()
        Dim workerThread1 As New Thread(AddressOf Worker)

        workerThread1.Start()
        Thread.Sleep(1000)

        Console.WriteLine("Interrupting worker1 at {0}", _
         DateTime.Now.ToLongTimeString())

        workerThread1.Interrupt()

        Dim workerThread2 As New Thread(AddressOf Worker)
        workerThread2.Start()
        Thread.Sleep(1000)

        Console.WriteLine("Calling Exit")

        Environment.Exit(0)
    End Sub

End Module

You first create a thread and interrupt it after a one-second delay. From the output shown in Figure 7-6, you can see that the thread does get interrupted. Then you create another thread and after a one-second delay you call Exit(). Note that the program terminates abruptly without giving the thread a chance to respond. The CLR terminates the process no matter which thread calls Exit(). This is undesirable. You might consider using Exit() in your application under special conditions, such as after dealing with an unhandled fatal exception. However, you need to exercise extreme caution in using Exit().

Output from Example 7-5

Figure 7-6. Output from Example 7-5

IN A NUTSHELL

Thoroughly understand the consequence of calling Exit() on a thread—you are bringing down the entire process.

GOTCHA #54 ResetAbort() may lead to surprises

In Gotcha #52, "ThreadAbortException—a hot potato,” you saw the special nature of ThreadAbortException. The Thread class provides the interesting method ResetAbort() to overrule this exception. Let’s start by reviewing the code in Example 7-6.

Example 7-6. ResetAbort() at work

C# (ResetAbortMethod)

using System;
using System.Threading;

namespace ResetAbort
{
    class Test
    {
        private static void Worker()
        {
            try
            {
                Thread.Sleep(5000);
            }
            catch(ThreadAbortException)
            {
                Console.WriteLine(
                    "ThreadAbortException caught");
                Thread.ResetAbort();
            }

            Console.WriteLine("Look where we are now!");

            Thread.Sleep(10000);
        }

        [STAThread]
        static void Main(string[] args)
        {
            Thread workerThread
                = new Thread(new ThreadStart(Worker));
            workerThread.Start();

            Thread.Sleep(1000);
            Console.WriteLine("Calling abort");
            workerThread.Abort();

            Thread.Sleep(2000);
            Console.WriteLine("Main done");
        }
    }
}

VB.NET (ResetAbortMethod)

Imports System.Threading

Module Test
    Private Sub Worker()
        Try
            Thread.Sleep(5000)
        Catch ex As ThreadAbortException

            Console.WriteLine("ThreadAbortException caught")
            Thread.ResetAbort()
        End Try

        Console.WriteLine("Look where we are now!")

        Thread.Sleep(10000)
    End Sub

    Public Sub Main()
        Dim workerThread As New Thread(AddressOf Worker)
        workerThread.Start()

        Thread.Sleep(1000)
        Console.WriteLine("Calling abort")
        workerThread.Abort()

        Thread.Sleep(2000)
        Console.WriteLine("Main done")
    End Sub

End Module

When the CLR throws the ThreadAbortException, within the catch block you call ResetAbort(). This cancels the Abort() and the method continues running, as you can see in Figure 7-7.

Output from Example 7-6

Figure 7-7. Output from Example 7-6

What’s the issue here? The problem with ResetAbort() is that the code that calls Abort() won’t know that the Thread has cancelled it. This can lead to unexpected (and unpredictable) behavior. If you find yourself doing something like this, you might need to redesign. Look at what you are trying to achieve and evaluate other ways to accomplish it (such as synchronization objects).

IN A NUTSHELL

ResetAbort() cancels an Abort(). This is counter to normal expectations. Avoid using it and find clearer ways to achieve your goal.

GOTCHA #55 Abort() takes time to clean up

As you saw in Gotcha #52, "ThreadAbortException—a hot potato,” when you abort a Thread, the CLR throws a ThreadAbortException on it. The thread can do whatever cleanup it needs to by handling the exception. The CLR then executes all the finally blocks in the thread’s call stack before actually terminating it. So there might be a delay between the time you call Abort() and the time the thread quits. This is illustrated in Example 7-7.

Example 7-7. Delay during Abort

C# (JoinAbort)

using System;
using System.Threading;

namespace AbortAndJoin
{
    class Test
    {
        private static void Worker()
        {
            Console.WriteLine("Worker started");
            try
            {
                Thread.Sleep(5000);
            }
            finally
            {
                Console.WriteLine("Worker enters finally {0}",
                    DateTime.Now.ToLongTimeString());
                Thread.Sleep(10000);
                // Simulates some cleanup activity
                Console.WriteLine("Cleanup done in Worker {0}",
                    DateTime.Now.ToLongTimeString());
            }
        }

        [STAThread]
        static void Main(string[] args)
        {
            Thread workerThread
                = new Thread(new ThreadStart(Worker));
            workerThread.IsBackground = true;
            workerThread.Start();
            Thread.Sleep(1000);

            Console.WriteLine("Aborting thread {0}",
                DateTime.Now.ToLongTimeString());
            workerThread.Abort();

            workerThread.Join();
            Console.WriteLine("Thread has aborted {0}",
                DateTime.Now.ToLongTimeString());
        }
    }
}

VB.NET (JoinAbort)

Imports System.Threading

Module Test

    Private Sub Worker()
        Console.WriteLine("Worker started")
        Try
            Thread.Sleep(5000)
        Finally
            Console.WriteLine("Worker enters finally {0}", _
                DateTime.Now.ToLongTimeString())
            Thread.Sleep(10000)
            ' Simulates some cleanup activity
            Console.WriteLine("Cleanup done in Worker {0}", _
                DateTime.Now.ToLongTimeString())
        End Try
    End Sub

    Public Sub Main()
        Dim workerThread As New Thread(AddressOf Worker)

        workerThread.IsBackground = True
        workerThread.Start()
        Thread.Sleep(1000)

        Console.WriteLine("Aborting thread {0}", _
         DateTime.Now.ToLongTimeString())
        workerThread.Abort()

        workerThread.Join()
        Console.WriteLine("Thread has aborted {0}", _
         DateTime.Now.ToLongTimeString())
    End Sub

End Module

In this example, the Worker() method’s finally block introduces a delay to simulate some activity. Main() first starts a thread to run the Worker() method. After a delay, it invokes Abort() on that thread. Then it calls Join() to wait for cleanup and completion. The output is shown in Figure 7-8.

Output from Example 7-7

Figure 7-8. Output from Example 7-7

If your application requires you to Abort() a thread and perform some operation after that thread has quit, you should call Join() on the thread after calling Abort(). This waits for the thread to clean up properly and exit gracefully before you continue processing.

Tip

You may want to call the Join() method with a reasonable timeout to avoid any potential starvation or deadlock, as in:

    workerThread.Join(2000);  // Wait two seconds for
                         // the thread to complete

IN A NUTSHELL

Ask yourself if you should call Join() after a call to Abort(). That is, ask if you need to wait for the thread to actually quit.

GOTCHA #56 Calling Type.GetType() may not return what you expect

Say you have a static/Shared method and you want to make sure that only one thread at a time can perform that operation. Typically, you use lock/SyncLock on an object to define a critical section. However, this won’t help if you are trying to synchronize within a static/Shared method or you’re trying to synchronize access to a static/Shared field. Static/Shared members are, by definition, independent from any object of the type. Consider Example 7-8.

Example 7-8. Ineffective synchronization

C# (Synchronizing)

//Bacteria.cs

using System;
using System.Threading;

namespace SynchOnType
{
    public class Bacteria
    {
        private static int bacteriaCount;

        private static void IncreaseCount()
        {
            Console.WriteLine(
                "IncreaseCount called by {0} at {1}",
                AppDomain.GetCurrentThreadId(),
                DateTime.Now.ToLongTimeString());

            bacteriaCount++;

            Thread.Sleep(2000);
            // Used for illustration purpose
        }

        public Bacteria()
        {
            lock(this)
            {
                IncreaseCount();
            }
        }
    }
}


//Test.cs
using System;
using System.Threading;

namespace SynchOnType
{
    class Test
    {
        private static void Worker()
        {
            Console.WriteLine("In thread {0}",
                AppDomain.GetCurrentThreadId());

            Bacteria aBacteria = new Bacteria();
        }

        [STAThread]
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(new ThreadStart(Worker));
            Thread thread2 = new Thread(new ThreadStart(Worker));

            thread1.Start();
            thread2.Start();
        }
    }
}

VB.NET (Synchronizing)

'Bacteria.vb

Imports System.Threading

Public Class Bacteria
    Private Shared bacteriaCount As Integer

    Private Shared Sub IncreaseCount()
        Console.WriteLine( _
            "IncreaseCount called by {0} at {1}", _
            AppDomain.GetCurrentThreadId(), _
            DateTime.Now.ToLongTimeString())

        bacteriaCount += 1

        Thread.Sleep(2000)
        ' Used for illustration purpose
    End Sub
    Public Sub New()
        SyncLock Me
            IncreaseCount()
        End SyncLock
    End Sub
End Class


'Test.vb

Imports System.Threading

Module Test

    Private Sub Worker()
        Console.WriteLine("In thread {0}", _
            AppDomain.GetCurrentThreadId())

        Dim aBacteria As New Bacteria
    End Sub

    Public Sub Main()
        Dim thread1 As New Thread(AddressOf Worker)
        Dim thread2 As New Thread(AddressOf Worker)

        thread1.Start()
        thread2.Start()
    End Sub
End Module

In this example, two threads create an object of the Bacteria class almost at the same time. Within the constructor of Bacteria you call a static/Shared method that requires synchronization, and you lock the instance before invoking the method.

The synchronization in the constructor is fundamentally flawed. The two threads in Test create one Bacteria object each. Each of the threads locks a different instance of Bacteria, and then calls the static method. When the program is executed, the output shown in Figure 7-9 is generated.

Output from Example 7-8

Figure 7-9. Output from Example 7-8

As you can see, the two threads execute the IncreaseCount() method at the same time, showing that neither is waiting the Sleep() time (2000 milliseconds) for the other as expected. The synchronization is ineffective.

For two threads to treat a code segment as a critical section, they need to synchronize on the same instance. What is common to both the instances of Bacteria? It’s the Bacteria class itself, isn’t it? So, why not synchronize on the Bacteria class, i.e., the metadata? The code that does just that is shown in Example 7-9.

Example 7-9. Synchronizing on the metadata

C# (Synchronizing)

        public Bacteria()
        {
            lock(GetType())
            {
                IncreaseCount();
            }
        }

VB.NET (Synchronizing)

   Public Sub New()
        SyncLock Me.GetType()
            IncreaseCount()
        End SyncLock
    End Sub

In the constructor of Bacteria you synchronize on the metadata of Bacteria. You obtain this object by calling the GetType() method on the instance. The output after the above change is shown in Figure 7-10.

Output after change shown in Example 7-9

Figure 7-10. Output after change shown in Example 7-9

The two threads are executing the IncreaseCount() method two seconds apart, indicating that the calls to the method are being synchronized. It works, doesn’t it? Are you done? Yep, let’s ship it.

Well, sorry, the euphoria is short-lived. Let’s write a new class and change the Test class, as in Example 7-10.

Example 7-10. Problem with instance’s GetType()

C# (Synchronizing)

//SpecializedBacteria.cs
 using System;

namespace SynchOnType
{
    public class SpecializedBacteria : Bacteria
    {
    }
}


//Test.cs
using System;
using System.Threading;

namespace SynchOnType
{
    class Test
    {
        private static void Worker1()
        {
            Console.WriteLine("In thread {0}",
                AppDomain.GetCurrentThreadId());

            Bacteria aBacteria = new Bacteria();
        }

        private static void Worker2()
        {
            Console.WriteLine("In thread {0}",
                AppDomain.GetCurrentThreadId());

            SpecializedBacteria aBacteria
                = new SpecializedBacteria();
        }

        [STAThread]
        static void Main(string[] args)
        {
            Thread thread1
                = new Thread(new ThreadStart(Worker1));
            Thread thread2
                = new Thread(new ThreadStart(Worker2));

            thread1.Start();
            thread2.Start();
        }
    }
}

VB.NET (Synchronizing)

'SpecializedBacteria.vb

Public Class SpecializedBacteria
    Inherits Bacteria
End Class


'Test.vb

Imports System.Threading

Module Test

    Private Sub Worker1()
        Console.WriteLine("In thread {0}", _
            AppDomain.GetCurrentThreadId())

        Dim aBacteria As New Bacteria
    End Sub

    Private Sub Worker2()
        Console.WriteLine("In thread {0}", _
            AppDomain.GetCurrentThreadId())

        Dim aBacteria As New SpecializedBacteria
    End Sub


    Public Sub Main()
        Dim thread1 As New Thread(AddressOf Worker1)
        Dim thread2 As New Thread(AddressOf Worker2)

        thread1.Start()
        thread2.Start()
    End Sub
End Module

In this code, thread1 calls Worker1(), which creates an instance of Bacteria. In the meantime, thread2 calls Worker2(), which creates an instance of SpecializedBacteria. Recall that the constructor of Bacteria is called when you create an instance of SpecializedBacteria. Within the constructor, when you call GetType(), which Type metadata is returned? Because the instance being created is SpecializedBacteria, GetType() returns the Type metadata for SpecializedBacteria, not that of Bacteria. Once again, the two threads end up locking different instances and get into the IncreaseCount() method at the same time, as shown in Figure 7-11.

Output from Example 7-10

Figure 7-11. Output from Example 7-10

In this situation, locking on the metadata returned by GetType() has no effect. What can you do to solve this problem? You want to lock the Bacteria class. So, why not do that explicitly? Consider Example 7-11.

Example 7-11. Explicitly locking the right metadata

C# (Synchronizing )

        public Bacteria()
        {
            lock(typeof(Bacteria))
            {
                IncreaseCount();
            }
        }

VB.NET (Synchronizing)

    Public Sub New()
        SyncLock GetType(Bacteria)
            IncreaseCount()
        End SyncLock
    End Sub

Here you fetch the Bacteria’s Type metadata by using the typeof operator in C# and the GetType() operator in VB.NET. This call returns a consistent metadata object regardless of the instance type. Now, when you execute the code, you get the desired output, as shown in Figure 7-12.

Output from Example 7-11

Figure 7-12. Output from Example 7-11

Tip

In this specific example, if your only objective is to synchronize the incrementing of the field, use Interlocked.Increment().

Are you done? Is this a solution you can be comfortable with? Well, not fully, but go with it for now while you look at more issues in the next gotcha.

IN A NUTSHELL

If your intent is to fetch the metadata for a specific class, use the typeof/GetType operator instead of the GetType() instance method.

GOTCHA #57 Locking on globally visible objects is too sweeping

In Gotcha #56, "Calling Type.GetType() may not return what you expect,” I discussed options for synchronizing threads. While the last solution presented there seems to work for that specific example, there is still a problem. Locking the type metadata to define a critical section is much too sweeping. If all static/Shared methods synchronize on the type metadata, then all access to the class methods is one-at-a-time, even if it doesn’t need to be. This certainly provides thread safety, but sacrifices concurrency in ways that are neither necessary nor desirable in most cases. Consider Example 7-12.

Example 7-12. Consequence of locking the Type metadata

C# (SynchWithIntent)

//Bacteria.cs
using System;
using System.Threading;

namespace SynchOnType
{
    public class Bacteria
    {
        private static int bacteriaCount;

        private static void IncreaseCount()
        {
            Console.WriteLine(
                "IncreaseCount called by {0} at {1}",
                AppDomain.GetCurrentThreadId(),
                DateTime.Now.ToLongTimeString());

            bacteriaCount++;

            Thread.Sleep(2000);
            // Used for illustration purpose
        }

        public Bacteria()
        {
            lock(typeof(Bacteria))
            {
                IncreaseCount();
            }
        }
    }
}


//Test.cs
using System;
using System.Threading;

namespace SynchOnType
{
    class Test
    {
        private static void Worker()
        {
            Console.WriteLine("In thread {0}",
                AppDomain.GetCurrentThreadId());

            Bacteria aBacteria = new Bacteria();
        }

        [STAThread]
        static void Main(string[] args)
        {
            Thread thread1 = new Thread(new ThreadStart(Worker));
            Thread thread2 = new Thread(new ThreadStart(Worker));

            lock(typeof(Bacteria))
            {
                Console.WriteLine("Starting threads at {0}",
                    DateTime.Now.ToLongTimeString());

                thread1.Start();
                thread2.Start();

                Thread.Sleep(3000);
            }
        }
    }
}

VB.NET (SynchWithIntent)

Imports System.Threading

'Bacteria.vb
Public Class Bacteria

    Private Shared bacteriaCount As Integer

    Private Shared Sub IncreaseCount()
        Console.WriteLine( _
         "IncreaseCount called by {0} at {1}", _
         AppDomain.GetCurrentThreadId(), _
         DateTime.Now.ToLongTimeString())

        bacteriaCount += 1

        Thread.Sleep(2000)
        ' Used for illustration purpose
    End Sub

    Public Sub New()
        SyncLock GetType(Bacteria)
            IncreaseCount()
        End SyncLock
    End Sub

End Class


'Test.vb

Imports System.Threading

Module Test

    Private Sub Worker()
        Console.WriteLine("In thread {0}", _
            AppDomain.GetCurrentThreadId())

        Dim aBacteria As New Bacteria
    End Sub
    Public Sub Main()
        Dim thread1 As New Thread(AddressOf Worker)
        Dim thread2 As New Thread(AddressOf Worker)

        SyncLock (GetType(Bacteria))
            Console.WriteLine("Starting threads at {0}", _
                 DateTime.Now.ToLongTimeString())

            thread1.Start()
            thread2.Start()

            Thread.Sleep(3000)
        End SyncLock
    End Sub

End Module

When you execute the above program, since the Main() method holds a lock on the Bacteria type for 3 seconds, the creation of the Bacteria objects is delayed until Main() lets go of that lock. One reason why a client of the Bacteria class might hold a lock on its metadata is so it can call multiple static/Shared methods in a thread-safe way. For whatever reason, if Main() or any other method in the application grabs a lock on the Bacteria metadata, the execution of the constructors (and all other methods) is delayed. This is shown in the output in Figure 7-13.

Output from Example 7-12

Figure 7-13. Output from Example 7-12

The problem here is that Bacteria’s constructor claims a lock on the Bacteria type in order to synchronize the execution of its static/Shared method. The static/Shared method and the implementation of the constructor are purely private to the class. However, you rely on a publicly visible object (the metadata of Bacteria) to synchronize. You can lock much more locally and precisely. Let’s look at a modified implementation in Example 7-13.

Example 7-13. Synchronizing locally and precisely

C# (SynchWithIntent )

//Bacteria.cs
using System;

namespace SynchOnType
{
    public class Bacteria
    {
        private static int bacteriaCount;
        private static object theIncrementCountLock = new Object();

        private static void IncreaseCount()
        {
            Console.WriteLine(
                "IncreaseCount called by {0} at {1}",
                AppDomain.GetCurrentThreadId(),
                DateTime.Now.ToLongTimeString());

            bacteriaCount++;

            System.Threading.Thread.Sleep(2000);
            // Used for illustration purpose
        }

        public Bacteria()
        {
            lock(theIncrementCountLock)
            {
                IncreaseCount();
            }
        }
    }
}

VB.NET (SynchWithIntent)

Imports System.Threading

'Bacteria.vb
Public Class Bacteria

    Private Shared bacteriaCount As Integer
    Private Shared theIncrementCountLock As New Object

    Private Shared Sub IncreaseCount()
        Console.WriteLine( _
         "IncreaseCount called by {0} at {1}", _
         AppDomain.GetCurrentThreadId(), _
         DateTime.Now.ToLongTimeString())

        bacteriaCount += 1

        Thread.Sleep(2000)
        ' Used for illustration purpose
    End Sub

    Public Sub New()
        SyncLock theIncrementCountLock
            IncreaseCount()
        End SyncLock
    End Sub

End Class

In this case, you have created a dummy object (theIncrementCountLock refers to it) within the Bacteria class. In the constructor, you lock this object. Note that the dummy lock-facilitator object is private, so no method outside the class can claim a lock with it.

There are two advantages to this approach. One, code in other classes can’t affect the concurrency of the constructor, because they can’t see theIncrementCountLock. Second, suppose you have two different tasks, call them Task A and Task B. Each of these tasks must be executed one-at-a-time. But if Task A doesn’t access any resources that Task B needs, and Task B touches none of Task A’s resources, there is no problem with their running concurrently. You can realize this most effectively using two private dummy-lock objects. The output after this change is shown in Figure 7-14.

Output after change in Example 7-13

Figure 7-14. Output after change in Example 7-13

For a great discussion on possible deadlock consequences, refer to the article "Don’t Lock Type Objects!" in the "on the web" section of the Appendix.

IN A NUTSHELL

Do not synchronize within your class on publicly visible objects. Synchronize precisely by synchronizing locally.

GOTCHA #58 Threads from the thread pool are scarce

Should you create a thread or use a thread from the thread pool? There are several considerations here.

First, if you create a Thread object, it will start to execute the method of interest within a fairly brief interval (subject to system limitations). In the case of the thread pool, however, you are using shared resources in your process. If another task that uses the thread pool is taking a little longer, your task may get delayed because there is no available thread in the thread pool.

At any given time up to twenty five threads are available per process per processor. You can change this default value at the system level. What does that mean to your application? If your tasks take a short amount of time and you have only a few of them, it is efficient to use the thread pool. However, if your tasks may take an arbitrary amount of time, then using the thread pool may not be a good idea.

Let’s get a better understanding of these issues from Example 7-14.

Example 7-14. Using your own thread versus one from thread pool

C# (ThreadFromPool)

using System;
using System.Threading;

namespace ThreadPool
{
    class Test
    {
        private static void Method1()
        {
            Console.Write("Executed by Thread {0} which is ",
                AppDomain.GetCurrentThreadId());

            if (!Thread.CurrentThread.IsThreadPoolThread)
                Console.Write("not ");

            Console.WriteLine("from the thread pool at {0}",
                DateTime.Now.ToLongTimeString());

            //Thread.Sleep(10000);
        }

        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("Using our own thread");
            for(int i = 0; i < 5; i++)
            {
                new Thread(new ThreadStart(Method1)).Start();
                Thread.Sleep(1000);
            }

            Console.WriteLine("Press any key to use timer");
            Console.ReadLine();
            Console.WriteLine("Using timer");
            System.Timers.Timer theTimer
                = new System.Timers.Timer(1000);
            theTimer.Elapsed
                += new System.Timers.ElapsedEventHandler(
                        theTimer_Elapsed);
            theTimer.Start();
            Thread.Sleep(6000);
            theTimer.Stop();
        }

        private static void theTimer_Elapsed(
            object sender, System.Timers.ElapsedEventArgs e)
        {
            Method1();
        }
    }
}

VB.NET (ThreadFromPool)

Imports System.Threading

Module Test

 Private Sub Method1()
 Console.Write("Executed by Thread {0} which is ", _
 AppDomain.GetCurrentThreadId())

 If Not Thread.CurrentThread.IsThreadPoolThread Then
 Console.Write("not ")
 End If

 Console.WriteLine("from the thread pool at {0}", _
 DateTime.Now.ToLongTimeString())

 'Thread.Sleep(10000)
 End Sub

 Public Sub Main()
 Console.WriteLine("Using our own thread")
 For i As Integer = 0 To 4

 Dim aThread As New Thread(AddressOf Method1)
 aThread.Start()
 Thread.Sleep(1000)
 Next

 Console.WriteLine("Press any key to use timer")
 Console.ReadLine()
 Console.WriteLine("Using timer")
 Dim theTimer As New System.Timers.Timer(1000)
 AddHandler theTimer.Elapsed, _
 New System.Timers.ElapsedEventHandler( _
 AddressOf theTimer_Elapsed)

 theTimer.Start()
 Thread.Sleep(6000)
 theTimer.Stop()
 End Sub

 Private Sub theTimer_Elapsed( _
 ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
 Method1()
 End Sub

End Module

In the previous example, you first run Method1() using threads you create. Then you run the method using a System.Timers.Timer object, which uses a thread from the thread pool. The output from the program is shown in Figure 7-15.

Notice that all the calls to Method1() using the timer were executed by the same thread from the thread pool. Now, what happens if you put a delay in the method? Executing the code after uncommenting Thread.Sleep(10000), you get the output shown in Figure 7-16.

Output from Example 7-14

Figure 7-15. Output from Example 7-14

Output from Example 7-14 after introducing a delay

Figure 7-16. Output from Example 7-14 after introducing a delay

Different threads from the thread pool now execute the method. However, the calls to the method using the thread pool are not distributed evenly once per second (note that two calls to Method1() were made at 8:19:48 PM).

Furthermore, let’s see what happens if you change Thread.Sleep(10000) to Thread.Sleep(35000) and let the Timer run for more than 35 seconds. Partial output from this change is shown in Example 7-15.

Example 7-15. Partial output upon increasing the delay in Method1()

Using timer
Executed by Thread 2864 which is from the thread pool at 8:21:55 PM
Executed by Thread 2196 which is from the thread pool at 8:21:57 PM
Executed by Thread 4064 which is from the thread pool at 8:21:57 PM
Executed by Thread 1536 which is from the thread pool at 8:21:58 PM
Executed by Thread 2492 which is from the thread pool at 8:21:59 PM
Executed by Thread 3024 which is from the thread pool at 8:22:00 PM
Executed by Thread 2588 which is from the thread pool at 8:22:01 PM
Executed by Thread 3000 which is from the thread pool at 8:22:02 PM
Executed by Thread 2932 which is from the thread pool at 8:22:03 PM
Executed by Thread 3952 which is from the thread pool at 8:22:04 PM
Executed by Thread 3004 which is from the thread pool at 8:22:05 PM
...
Executed by Thread 2524 which is from the thread pool at 8:22:11 PM
Executed by Thread 3016 which is from the thread pool at 8:22:12 PM
Executed by Thread 3012 which is from the thread pool at 8:22:13 PM
Executed by Thread 3160 which is from the thread pool at 8:22:14 PM
Executed by Thread 3156 which is from the thread pool at 8:22:15 PM
Executed by Thread 2992 which is from the thread pool at 8:22:16 PM
Executed by Thread 3008 which is from the thread pool at 8:22:17 PM
Executed by Thread 2984 which is from the thread pool at 8:22:18 PM
Executed by Thread 3040 which is from the thread pool at 8:22:19 PM
Executed by Thread 2864 which is from the thread pool at 8:22:30 PM
Executed by Thread 2196 which is from the thread pool at 8:22:32 PM
Executed by Thread 4064 which is from the thread pool at 8:22:32 PM
Executed by Thread 1536 which is from the thread pool at 8:22:33 PM
Executed by Thread 2492 which is from the thread pool at 8:22:34 PM

The threads in the thread pool are reused once they become available. This is of course good news. However, notice the lag time between the method executions when all the threads in the thread pool are busy.

IN A NUTSHELL

The thread pool offers a very efficient use of resources, but it should only be used for tasks that are quick and short-lived.

GOTCHA #59 Threads invoked using delegates behave like background threads

If you want a method to be executed from another thread, you can create a thread, set it to background, and then start it. That is three lines of code at least, isn’t it? What if your method takes parameters? That is even more work. This is where the Delegate class’s BeginInvoke() and EndInvoke() methods look very enticing. Delegates provide BeginInvoke() and EndInvoke() to call a method asynchronously. The intended method is invoked from another thread, and the code is pretty easy to write.

Let’s see how you can do just that in Example 7-16.

Example 7-16. Using Delegate to call methods in another thread

C# (BeginInvoke )

using System;

namespace DelegateThread
{
    class Test
    {
        private static void Method1(int val)
        {
            Console.Write("Method 1 called from Thread {0}",
                AppDomain.GetCurrentThreadId());
            Console.WriteLine(" with value {0}", val);
        }

        delegate void Method1Delegate(int val);

        [STAThread]
        static void Main(string[] args)
        {
            // It is not so easy to call Method1 from
            // another thread using the Thread class.

            Method1Delegate dlg = new Method1Delegate(Method1);
            dlg.BeginInvoke(2, null, null);

            Console.ReadLine();
        }
    }
}

VB.NET (BeginInvoke)

Module Test

    Private Sub Method1(ByVal val As Integer)
        Console.Write("Method 1 called from Thread {0}", _
            AppDomain.GetCurrentThreadId())

        Console.WriteLine(" with value {0}", val)
    End Sub

    Delegate Sub Method1Delegate(ByVal val As Integer)

    Public Sub Main()
        ' It is not so easy to call Method1 from
        ' another thread using the Thread class.

        Dim dlg As New Method1Delegate(AddressOf Method1)
        dlg.BeginInvoke(2, Nothing, Nothing)

        Console.ReadLine()

    End Sub

End Module

In this example, you simply create an object of the delegate Method1Delegate and call its BeginInvoke() method, passing the desired value for its first argument. The two null/Nothing arguments indicate that you are not passing a callback method or AsyncState object. The output is shown in Figure 7-17.

Output from Example 7-16

Figure 7-17. Output from Example 7-16

If you are able to execute the method in another thread so easily, why would you do it any other way? Well, this is perfectly OK, but you need to understand the benefits and drawbacks:

  • The method is executed by a thread from the thread pool.

  • The method is executed by a thread that is marked as a background thread.

  • Exceptions thrown from the method are lost unless you call EndInvoke(), even for methods that do not return any results.

What do these considerations imply? Since you use a thread from the thread pool, its execution depends on the availability of a thread (see “Gotcha #58, "Threads from the thread pool are scarce). Since it is a background thread, it will be terminated if all foreground threads terminate (see Gotcha #49, "Foreground threads may prevent a program from terminating" and Gotcha #50, "Background threads don’t terminate gracefully“). Also, since it is from the thread pool, any exceptions thrown in it are handled by the pool. As a result you may not even realize that an exception occurred until you call EndInvoke() (see Gotcha #61, "Exceptions thrown from threads in the pool are lost“). With a C# void method, or a VB.NET Sub, you normally have no reason to call EndInvoke(). But you have to if you want to know if the method completed successfully. Finally, you don’t want to synchronize with any other threads while running in the context of a thread-pool thread. If you do, it may delay the start of other tasks that need a thread from the thread pool.

IN A NUTSHELL

Fully understand the behavior and limitations of using Delegate.BeginInvoke(). It has all the advantages and limitations of using a background thread and a thread from the thread pool.

GOTCHA #60 Passing parameters to threads is tricky

Say you want to call a method of your class in a separate thread of execution. To start a new thread, you create a Thread object, provide it with a ThreadStart delegate, and call the Start() method on the Thread instance. The ThreadStart delegate, however, only accepts methods that take no parameters. So how can you pass parameters to the thread?

One option is to use a different Delegate (as discussed in Gotcha #59, "Threads invoked using delegates behave like background threads“) and call the method asynchronously using the Delegate.BeginInvoke() method. This is by far the most convenient option. However, this executes the method in a thread from the thread pool. Therefore, its time of execution has to stay pretty short. Otherwise you end up holding resources from the thread pool and may slow down the start of other tasks.

Let’s consider Example 7-17, in which you want to start a thread and pass it an integer parameter.

Example 7-17. Calling method with parameter from another thread

C# (ParamThreadSafety)

//SomeClass.cs part of ALib.dll
using System;
using System.Threading;

namespace ALib
{
    public class SomeClass
    {
        private void Method1(int val)
        {
            // Some operation takes place here
            Console.WriteLine(
                "Method1 runs on Thread {0} with {1}",
                AppDomain.GetCurrentThreadId(), val);
        }

        public void DoSomething(int val)
        {
            // Some operation...

            // Want to call Method1 in different thread
            // from here?

            // Some operation...
        }
    }
}


//Test.cs part of TestApp.exe
using System;
using ALib;

namespace TestApp
{
    class Test
    {
        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("Main running in Thread {0}",
                AppDomain.GetCurrentThreadId());
            SomeClass anObject = new SomeClass();
            anObject.DoSomething(5);
        }
    }
}

VB.NET (ParamThreadSafety)

'SomeClass.vb part of ALib.dll
Imports System.Threading

Public Class SomeClass

    Private Sub Method1(ByVal val As Integer)
        Console.WriteLine( _
            "Method1 runs on Thread {0} with {1}", _
            AppDomain.GetCurrentThreadId(), val)

        ' Some operation takes place here
    End Sub

    Public Sub DoSomething(ByVal val As Integer)
        ' Some operation...

        ' Want to call Method1 in different thread
        ' from here?

        ' Some operation...
    End Sub
End Class


'Test.vb

Imports ALib

Module Test
    Public Sub Main()
        Console.WriteLine("Main running in Thread {0}", _
           AppDomain.GetCurrentThreadId())
        Dim anObject As New SomeClass
        anObject.DoSomething(5)
    End Sub
End Module

In this example, SomeClass.DoSomething() wants to call Method1() in a different thread. Unfortunately, you can’t just create a new Thread instance and pass a ThreadStart delegate with the address of Method1(). How can you invoke Method1() from here? One approach is shown in Example 7-18.

Example 7-18. One approach to invoking method with parameter

C# (ParamThreadSafety)

//SomeClass.cs part of ALib.dll
using System;
using System.Threading;

namespace ALib
{
    public class SomeClass
    {
        private void Method1(int val)
        {
            // Some operation takes place here
            Console.WriteLine(
                "Method1 runs on Thread {0} with {1}",
                AppDomain.GetCurrentThreadId(), val);
        }

        private int theValToUseByCallMethod1;

        private void CallMethod1()
        {
            Method1(theValToUseByCallMethod1);
        }

        public void DoSomething(int val)
        {
            // Some operation...

            // Want to call Method1 in different thread
            // from here?
            theValToUseByCallMethod1 = val;
            new Thread(new ThreadStart(CallMethod1)).Start();

            // Some operation...
        }
    }
}

VB.NET (ParamThreadSafety)

'SomeClass.vb part of ALib.dll
Imports System.Threading

Public Class SomeClass

    Private Sub Method1(ByVal val As Integer)
        Console.WriteLine( _
            "Method1 runs on Thread {0} with {1}", _
            AppDomain.GetCurrentThreadId(), val)

        ' Some operation takes place here
    End Sub

    Private theValToUseByCallMethod1 As Integer

    Private Sub CallMethod1()
        Method1(theValToUseByCallMethod1)
    End Sub


    Public Sub DoSomething(ByVal val As Integer)
        ' Some operation...

        ' Want to call Method1 in different thread
        ' from here?
        theValToUseByCallMethod1 = val
        Dim aThread As New Thread(AddressOf CallMethod1)
        aThread.Start()

        ' Some operation...
    End Sub
End Class

In the DoSomething() method, you first store the argument you want to pass to the thread in the private field theValToUseByCallMethod1. Then you call a no-parameter method CallMethod1() in a different thread. CallMethod1(), executing in this new thread, picks up the private field set by the main thread and calls Method1() with it. The output from the above code is shown in Figure 7-18.

Output from Example 7-18

Figure 7-18. Output from Example 7-18

See, it works! Well, yes, if a click of a button is going to call DoSomething(), then the chances of DoSomething() being called more than once before Method1() has had a chance to execute are slim. But if this is invoked from a class library, or from multiple threads in the UI itself, how thread-safe is the code? Not very.

Let’s add a line to the Main() method as shown in Example 7-19.

Example 7-19. Testing thread safety of approach in Example 7-18

C# (ParamThreadSafety)

        static void Main(string[] args)
        {
            Console.WriteLine("Main running in Thread {0}",
                AppDomain.GetCurrentThreadId());
            SomeClass anObject = new SomeClass();
            anObject.DoSomething(5);
            anObject.DoSomething(6);
        }

VB.NET (ParamThreadSafety)

    Public Sub Main()
        Console.WriteLine("Main running in Thread {0}", _
            AppDomain.GetCurrentThreadId())
        Dim anObject As New SomeClass
        anObject.DoSomething(5)
        anObject.DoSomething(6)
    End Sub

Here, you invoke the DoSomething() method with a value of 6 immediately after calling it with a value of 5. Let’s look at the output from the program after this change, shown in Figure 7-19.

Output from Example 7-19

Figure 7-19. Output from Example 7-19

As you can see, both calls to DoSomething() pass Method1() the value 6. The value of 5 you send in the first invocation is simply overwritten.

One way to attain thread safety in this situation is to isolate the value in a different object. This is shown in Example 7-20.

Example 7-20. Providing thread safety of parameter

C# (ParamThreadSafety )

        //...
        class CallMethod1Helper
        {
            private SomeClass theTarget;
            private int theValue;
            public CallMethod1Helper(int val, SomeClass target)
            {
                theValue = val;
                theTarget = target;
            }

            private void CallMethod1()
            {
                theTarget.Method1(theValue);
            }

            public void Run()
            {
                new Thread(
                    new ThreadStart(CallMethod1)).Start();
            }
        }

        public void DoSomething(int val)
        {
            // Some operation...

            // Want to call Method1 in different thread
            // from here?
            CallMethod1Helper helper = new CallMethod1Helper(
                val, this);
            helper.Run();

            // Some operation...
        }

VB.NET (ParamThreadSafety)

    '...
    Class CallMethod1Helper
        Private theTarget As SomeClass
        Private theValue As Integer

        Public Sub New(ByVal val As Integer, ByVal target As SomeClass)
            theValue = val
            theTarget = target
        End Sub

        Private Sub CallMethod1()
            theTarget.Method1(theValue)
        End Sub

        Public Sub Run()
            Dim theThread As New Thread(AddressOf CallMethod1)
            theThread.Start()
        End Sub
    End Class

    Public Sub DoSomething(ByVal val As Integer)
        ' Some operation...

        ' Want to call Method1 in different thread
        ' from here?
        Dim helper As New CallMethod1Helper(val, Me)
        helper.Run()

        ' Some operation...
    End Sub

In this case, you create a nested helper class ClassMethod1Helper that holds the val and a reference to the object of SomeClass. You invoke the Run() method on an instance of the helper in the original thread. Run() in turn invokes CallMethod1() of the helper in a separate thread. This method calls Method1(). Since the instance of helper is created within the DoSomething() method, multiple calls to DoSomething() will result in multiple helper objects being created on the heap. They are isolated from one another and provide thread safety for the parameter. The output from the program is shown in Figure 7-20.

Output from Example 7-20

Figure 7-20. Output from Example 7-20

In this example, it took over 20 lines of code (with proper indentation, that is) to create a thread-safe start of Method1(). If you need to invoke another method with parameters, you will have to write almost the same amount of code. You will end up writing a class for each method you want to call. This is quite a bit of redundant coding.

Why not write your own thread-safe thread starter? The code to start the thread might look like Example 7-21 (using the ThreadRunner class, which you’ll see shortly).

Example 7-21. Using ThreadRunner

C# (ParamThreadSafety)

//SomeClass.cs part of ALib.dll
using System;
using System.Threading;

namespace ALib
{
    public class SomeClass
    {
        private void Method1(int val)
        {
            // Some operation takes place here
            Console.WriteLine(
                "Method1 runs on Thread {0} with {1}",
                AppDomain.GetCurrentThreadId(), val);
        }

        private delegate void CallMethod1Delegate(int val);

        public void DoSomething(int val)
        {
            // Some operation...

            // Want to call Method1 in different thread
            // from here?
            ThreadRunner theRunner
                = new ThreadRunner(
                    new CallMethod1Delegate(Method1), val);

            theRunner.Start();

            // Some operation...
        }
    }
}

VB.NET (ParamThreadSafety)

'SomeClass.vb part of ALib.dll
Imports System.Threading

Public Class SomeClass

    Private Sub Method1(ByVal val As Integer)
        Console.WriteLine( _
            "Method1 runs on Thread {0} with {1}", _
            AppDomain.GetCurrentThreadId(), val)

        ' Some operation takes place here
    End Sub

    Private Delegate Sub CallMethod1Delegate(ByVal val As Integer)


    Public Sub DoSomething(ByVal val As Integer)
        ' Some operation...

        ' Want to call Method1 in different thread
        ' from here?
        Dim theRunner As New ThreadRunner( _
            New CallMethod1Delegate(AddressOf Method1), val)

        theRunner.Start()

        ' Some operation...
    End Sub
End Class

That is sweet and simple. You create a ThreadRunner object and send it a delegate with the same signature as the method you’re going to call. You also send it the parameters you want to pass. The ThreadRunner launches a new thread to execute the method that is referred to by the given delegate.

The code for ThreadRunner is shown in Example 7-22. Note that you do not write a ThreadRunner for each method you want to call. Unlike the CallMethod1Helper, this is a class written once and used over and over.

Example 7-22. ThreadRunner class

C# (ParamThreadSafety)

//ThreadRunner.cs
using System;
using System.Threading;

namespace ALib
{
    public class ThreadRunner
    {
        private Delegate toRunDelegate;
        private object[] toRunParameters;
        private Thread theThread;

        public bool IsBackground
        {
            get { return theThread.IsBackground; }
            set { theThread.IsBackground = value; }
        }

        public ThreadRunner(Delegate theDelegate,
            params object[] theParameters)
        {
            toRunDelegate = theDelegate;
            toRunParameters = theParameters;

            theThread = new Thread(new ThreadStart(Run));
        }

        public void Start()
        {
            theThread.Start();
        }

        private void Run()
        {
            toRunDelegate.DynamicInvoke(toRunParameters);
        }
    }
}

VB.NET (ParamThreadSafety)

'ThreadRunner.vb

Imports System.Threading

Public Class ThreadRunner
    Private toRunDelegate As System.Delegate

    Private toRunParameters() As Object

    Private theThread As Thread

    Public Property IsBackground() As Boolean
        Get
            Return theThread.IsBackground
        End Get
        Set(ByVal Value As Boolean)
            theThread.IsBackground = Value
        End Set
    End Property

    Public Sub New(ByVal theDelegate As System.Delegate, _
                   ByVal ParamArray theParameters() As Object)
        toRunDelegate = theDelegate
        toRunParameters = theParameters

        theThread = New Thread(AddressOf Run)
    End Sub

    Public Sub Start()
        theThread.Start()
    End Sub

    Private Sub Run()
        toRunDelegate.DynamicInvoke(toRunParameters)
    End Sub
End Class

Because ThreadRunner exposes the IsBackground property of its underlying Thread object, a user of ThreadRunner can set IsBackground to true if desired. To start the target method in a separate thread, the user of this class calls ThreadRunner.Start().

How does this differ in .NET 2.0 Beta 1? A new delegate named ParameterizedThreadStart is introduced. Using this new delegate, you may invoke methods that take one parameter by passing the argument to the Thread class’s Start() method.

IN A NUTSHELL

When you start a thread with a method that requires parameters, wrapper classes like ThreadRunner (see Example 7-22) can help ensure thread safety.

GOTCHA #61 Exceptions thrown from threads in the pool are lost

If any exceptions are thrown from a thread you create and you don’t handle them, the CLR reports them to the user. In a console application, a message is printed on the console. In a Windows application, a dialog appears with the details of the exception. Of course, a program that displays such unhandled exceptions is undesirable. However, you might also agree that such a program is better than a program that continues to execute and quietly misbehaves after things go wrong. Let’s look at Example 7-23.

Example 7-23. Unhandled exception

C# (ExceptionInThread)

using System;
using System.Threading;

namespace DelegateThread
{
    class Test
    {
        private static void Method1()
        {
            Console.WriteLine("Method1 is throwing exception");
            throw new ApplicationException("**** oops ****");
        }

        delegate void Method1Delegate();

        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("We first use a thread");
            Thread aThread
                = new Thread(new ThreadStart(Method1));
            aThread.Start();

            Console.WriteLine("press return");
            Console.ReadLine();

            Console.WriteLine("We will use a Delegate now");
            Method1Delegate dlg = new Method1Delegate(Method1);
            IAsyncResult handle = dlg.BeginInvoke(null, null);

            Thread.Sleep(1000);

            Console.WriteLine("Was the exception reported so far?");

            try
            {
                Console.WriteLine("Let's call EndInvoke");
                dlg.EndInvoke(handle);
            }
            catch(Exception ex)
            {
                Console.WriteLine("Exception: {0}", ex.Message);
            }

            Console.WriteLine("press return");
            Console.ReadLine();

            Console.WriteLine("We will use a timer now");

            System.Timers.Timer theTimer
                = new System.Timers.Timer(1000);
            theTimer.Elapsed
                += new System.Timers.ElapsedEventHandler(
                    theTimer_Elapsed);
            theTimer.Start();

            Thread.Sleep(3000);
            theTimer.Stop();

            Console.WriteLine("press return");
            Console.ReadLine();
        }

        private static void theTimer_Elapsed(
            object sender, System.Timers.ElapsedEventArgs e)
        {
            Method1();
        }
    }
}

VB.NET (ExceptionInThread)

Imports System.Threading

Module Test

 Private Sub Method1()
 Console.WriteLine("Method1 is throwing exception")
 Throw New ApplicationException("**** oops ****")
 End Sub

 Delegate Sub Method1Delegate()

 Public Sub Main()
 Console.WriteLine("We first use a thread")
 Dim aThread As New Thread(AddressOf Method1)
 aThread.Start()

 Console.WriteLine("press return")
 Console.ReadLine()

 Console.WriteLine("We will use a Delegate now")
 Dim dlg As New Method1Delegate(AddressOf Method1)

 Dim handle As IAsyncResult = dlg.BeginInvoke(Nothing, Nothing)

 Thread.Sleep(1000)

 Console.WriteLine("Was the exception reported so far?")

 Try
 Console.WriteLine("Let's call EndInvoke")
 dlg.EndInvoke(handle)
 Catch ex As Exception
 Console.WriteLine("Exception: {0}", ex.Message)
 End Try

 Console.WriteLine("press return")
 Console.ReadLine()

 Console.WriteLine("We will use a timer now")

 Dim theTimer As New System.Timers.Timer(1000)

 AddHandler theTimer.Elapsed, _
 New System.Timers.ElapsedEventHandler( _
 AddressOf theTimer_Elapsed)

 theTimer.Start()

 Thread.Sleep(3000)
 theTimer.Stop()

 Console.WriteLine("press return")
 Console.ReadLine()
 End Sub

 Private Sub theTimer_Elapsed( _
 ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)

 Method1()
 End Sub
End Module
VB.NET (ExceptionInThread)

In this example, you have a method Method1() that throws an exception. You first call this method from our own thread. The CLR reports this exception by displaying a message on the console. Then you invoke the method using Method1Delegate.BeginInvoke(). The exception is not caught or reported when it is thrown. It does surface eventually when you call the delegate’s EndInvoke(). The worst offender is the Timer, where the exception simply goes unnoticed. The output from the above program is shown in Figure 7-21.

Output from Example 7-23

Figure 7-21. Output from Example 7-23

Why is this important to know? Consider invoking a web service asynchronously. You set up a delegate to it and in the callback, you call EndInvoke(). The problem is that the callback itself is called from a thread in the thread pool. If an exception is raised in the callback, it is never seen. This is illustrated in Examples 7-24 through 7-27, and in Figures 7-22 and 7-23.

Example 7-24. Lost exception in asynchronous call, Web service (C#)

C# (ExceptionInThread), server-side code

//MyService.asmx.cs part of ACSWebService.dll (Web Service)
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;

namespace ACSWebService
{
    [WebService(Namespace="MyServiceNameSpace")]
    public class MyService : System.Web.Services.WebService
    {
        // ...

        [WebMethod]
        public int Method1(int val)
        {
            if (val == 0)
                throw new ApplicationException(
                    "Do not like the input");

            return val;
        }
    }
}
Main form (Form1) for Web Service client (C#)

Figure 7-22. Main form (Form1) for Web Service client (C#)

Example 7-25. Lost exception in asynchronous call, Web service client (C#)

C# (ExceptionInThread), client-side code

///Form1.cs part of AWSClient
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using AWSClient.ACSWebService;

namespace AWSClient
{
    public class Form1 : System.Windows.Forms.Form
    {
        private static MyService service;

        // Parts of this file not shown...

        private void InvokeButton_Click(
            object sender, System.EventArgs e)
        {

            service = new MyService();
            service.Credentials =
                System.Net.CredentialCache.DefaultCredentials;

            if (AsynchCheckBox.Checked)
            {
                CallAsynch();
            }
            else
            {
                CallSynch();
            }

        }

        private static void CallSynch()
        {
            int result = service.Method1(1);
            MessageBox.Show("Result received: " + result);

            result = service.Method1(0);
            MessageBox.Show("Result received: " + result);
        }

        private static void CallAsynch()
        {
            service.BeginMethod1(1,
                new AsyncCallback(response), service);

            Thread.Sleep(1000);

            service.BeginMethod1(0,
                new AsyncCallback(response), service);

            Thread.Sleep(1000);
        }

        private static void response(IAsyncResult handle)
        {
            MyService theService = handle.AsyncState as MyService;

            int result = theService.EndMethod1(handle);
            MessageBox.Show("Result received asynchronously " +
                result);
        }
    }
}

Example 7-26. Lost exception in asynchronous call, Web service (VB.NET)

VB.NET (ExceptionInThread), server-side code

'MyService.asmx.vb part of AVBWebService.dll (Web Service)
Imports System.Web.Services

<System.Web.Services.WebService(Namespace:="MyServiceNameSpace")> _
Public Class MyService
    Inherits System.Web.Services.WebService

    '...

    <WebMethod()> _
    Public Function Method1(ByVal val As Integer) As Integer
        If val = 0 Then
            Throw New ApplicationException("Do not like the input")
        End If

        Return val
    End Function

End Class
Main form (Form1) for Web Service (VB.NET)

Figure 7-23. Main form (Form1) for Web Service (VB.NET)

Example 7-27. Lost exception in asynchronous call, Web service client (VB.NET)

VB.NET (ExceptionInThread), client-side code

'Form1.vb part of AWSClient

Imports System.Threading
Imports AWSClient.AVBWebService

Public Class Form1
    Inherits System.Windows.Forms.Form

    Private Shared service As MyService

    'Parts of this file not shown...

    Private Sub InvokeButton_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles InvokeButton.Click

        service = New MyService
        service.Credentials = _
         System.Net.CredentialCache.DefaultCredentials

        If AsynchCheckBox.Checked Then
            CallAsynch()
        Else
            CallSynch()
        End If

    End Sub

    Private Shared Sub CallSynch()
        Dim result As Integer = service.Method1(1)
        MessageBox.Show("Result received: " & result)
        result = service.Method1(0)
        MessageBox.Show("Result received: " & result)
    End Sub

    Private Shared Sub CallAsynch()
        service.BeginMethod1(1, _
         New AsyncCallback(AddressOf response), service)

        Thread.Sleep(1000)

        service.BeginMethod1(0, _
         New AsyncCallback(AddressOf response), service)

        Thread.Sleep(1000)
    End Sub

    Private Shared Sub response(ByVal handle As IAsyncResult)
        Dim theService As MyService = _
            CType(handle.AsyncState, MyService)

        Dim result As Integer = theService.EndMethod1(handle)
        MessageBox.Show("Result received asynchronously " & _
            result)
    End Sub

End Class

The web service throws an exception if the parameter to Method1() is zero. From a Windows application, you invoke the web service, first sending a value of 1 and then a value of 0. When you invoke the method synchronously with an argument of zero, the CLR displays an exception, as shown in Figure 7-24.

Output from Example 7-24 for a synchronous call with a parameter of zero

Figure 7-24. Output from Example 7-24 for a synchronous call with a parameter of zero

However, if you call it asynchronously, no exception is reported. This is because the callback itself executes on a thread pool thread. You might expect the exception to be received when you call EndInvoke() in the response() method. Yes, an exception is raised (a System.Web.Services.Protocols.SoapException, to be precise). But it isn’t propagated to you; the CLR suppresses any exception raised from a thread in the thread pool. You can verify this by adding a try/catch block around the EndInvoke() call, as shown in Example 7-28.

Example 7-28. Catching exception after call to EndInvoke()

C# (ExceptionInThread), client-side code

        private static void response(IAsyncResult handle)
        {
            MyService theService
                = handle.AsyncState as MyService;

            try
            {
                int result = theService.EndMethod1(handle);
                MessageBox.Show("Result received asynchronously " +
                    result);
            }
            catch(System.Web.Services.Protocols.SoapException ex)
            {
                MessageBox.Show("Now I got " + ex);
            }
        }

VB.NET (ExceptionInThread), client-side code

    Private Shared Sub response(ByVal handle As IAsyncResult)
        Dim theService As MyService = _
            CType(handle.AsyncState, MyService)

        Try
            Dim result As Integer = theService.EndMethod1(handle)
            MessageBox.Show("Result received asynchronously " & _
                result)
        Catch ex As System.Web.Services.Protocols.SoapException
            MessageBox.Show("Now I got " & ex.ToString())
        End Try
    End Sub

Now when you make the call asynchronously, you get the message shown in Figure 7-25.

Exception reported after change shown in Example 7-28

Figure 7-25. Exception reported after change shown in Example 7-28

IN A NUTSHELL

When using threads from the thread pool, pay extra attention to exception handling. Exceptions may not be reported, depending on what type of object you use: Delegate or Timer.

GOTCHA #62 Accessing WinForm controls from arbitrary threads is dangerous

Typically, you execute a task in a thread other than the main thread if that task might take a long time, but meanwhile you want your application to be responsive, plus you want to be able to preempt the task. Once it has completed, how do you display the results in the form’s control? Keep in mind that the form’s controls are not thread-safe. The only methods and properties of a control that are thread-safe are BeginInvoke(), EndInvoke(), Invoke(), InvokeRequired, and CreateGraphics(). This is not a flaw. It was done by design to improve performance by eliminating thread-synchronization overhead. If you want to access any other methods or properties of a control, you should do so only from the thread that owns the control’s underlying window handle. This is typically the thread that created the control, which generally is the main thread in your application.

What happens if you access the non-thread-safe methods of a control from another thread? Unfortunately, the program may appear to work fine on several occasions. But just because the program runs, it does not mean it has no problems. In this case it is not a question of if, but of when the program will misbehave. These kinds of problems are difficult to predict and often may not be easily reproduced.

How can you call a method on a control from within another thread? You can do that using the System.Windows.Forms.Control.Invoke() method on the control. This method executes a delegate on the thread that owns the control’s window handle. The call to Invoke() blocks until that method completes.

Say you create a System.Windows.Forms.Timer in a Form. Should you access the controls of a Form from within the Timer’s callback method? What about from the callback of an asynchronous method call to a web service? The answers depend on understanding the thread in which each of these executes. Things become clearer when you examine the details.

An example will clarify this. Figure 7-26 shows a WinForm application with three buttons and a couple of labels.

A WinForm application to understand threads

Figure 7-26. A WinForm application to understand threads

The Timer button is linked to code that creates a System.Windows.Forms.Timer. It in turn executes a method, SetExecutingThreadLabel(), that provides some details on the executing thread.

Tip

It is important to distinguish this Timer, which resides in the System.Windows.Forms namespace, from the Timer class you’ve already seen, which is in System.Timers.Timers. (See Gotcha #58, "Threads from the thread pool are scarce" and Gotcha #61, "Exceptions thrown from threads in the pool are lost.”)

The Timers Timer button is linked to a handler that creates a System.Timers.Timer object. It raises an event that also executes SetExecutingThreadLabel(). Finally, the Delegate button is tied to a handler that creates a Delegate. It then calls its BeginInvoke(), which also executes SetExecutingThreadLabel(). The code is shown in Examples 7-29 and 7-30.

Example 7-29. Executing thread for different Timers and Delegate (C#)

C# (ControlThread)

        private void Form1_Load(object sender, System.EventArgs e)
        {
            MainThreadLabel.Text
                = AppDomain.GetCurrentThreadId().ToString();
        }

        private delegate void SetLabelDelegate(string message);

        private void SetExecutingThreadLabel(string message)
        {
            ExecutingThreadLabel.Text = message;
        }

        private void TimerButton_Click(
            object sender, System.EventArgs e)
        {
            Timer theTimer = new Timer();
            theTimer.Interval = 1000;
            theTimer.Tick += new EventHandler(Timer_Tick);
            theTimer.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            Invoke(new SetLabelDelegate(SetExecutingThreadLabel),
                new object[] {
                    "Timer : "
                    + AppDomain.GetCurrentThreadId() + ": "
                    + InvokeRequired });
            (sender as Timer).Stop();
        }

        private void TimersTimerButton_Click(
            object sender, System.EventArgs e)
        {
            System.Timers.Timer theTimer
                = new System.Timers.Timer(1000);
            theTimer.Elapsed
                += new System.Timers.ElapsedEventHandler(
                    Timer_Elapsed);
            theTimer.Start();
        }

        private void Timer_Elapsed(
            object sender, System.Timers.ElapsedEventArgs e)
        {
            Invoke(new SetLabelDelegate(SetExecutingThreadLabel),
                new object[] {
                    "Timers.Timer : "
                    + AppDomain.GetCurrentThreadId() + ": "
                    + InvokeRequired });
            (sender as System.Timers.Timer).Stop();
        }

        private delegate void AsynchDelegate();

        private void DelegateButton_Click(
            object sender, System.EventArgs e)
        {
            AsynchDelegate dlg
                = new AsynchDelegate(AsynchExecuted);
            dlg.BeginInvoke(null, null);
        }

        private void AsynchExecuted()
        {
            Invoke(new SetLabelDelegate(SetExecutingThreadLabel),
                new object[] {
                    "Delegate : "
                    + AppDomain.GetCurrentThreadId() + ": "
                    + InvokeRequired });
        }

Example 7-30. Executing thread for different Timers and Delegate (VB.NET)

VB.NET (ControlThread)

    Private Sub Form1_Load(ByVal sender As System.Object, _
           ByVal e As System.EventArgs) Handles MyBase.Load
        MainThreadLabel.Text _
            = AppDomain.GetCurrentThreadId().ToString()
    End Sub

    Private Delegate Sub SetLabelDelegate(ByVal message As String)

    Private Sub SetExecutingThreadLabel(ByVal message As String)
        ExecutingThreadLabel.Text = message
    End Sub

    Private Sub TimerButton_Click( _
           ByVal sender As System.Object, _
           ByVal e As System.EventArgs) _
           Handles TimerButton.Click
        Dim theTimer As Timer = New Timer
        theTimer.Interval = 1000
        AddHandler theTimer.Tick, _
            New EventHandler(AddressOf Timer_Tick)
        theTimer.Start()
    End Sub

    Private Sub Timer_Tick(ByVal sender As Object, _
           ByVal e As EventArgs)
        Invoke(New SetLabelDelegate( _
            AddressOf SetExecutingThreadLabel), _
         New Object() { _
          "Timer : " _
          & AppDomain.GetCurrentThreadId() & ": " _
          & InvokeRequired})
        CType(sender, Timer).Stop()
    End Sub

    Private Sub TimersTimerButton_Click( _
           ByVal sender As System.Object, _
           ByVal e As System.EventArgs) Handles TimersTimerButton.Click
        Dim theTimer As System.Timers.Timer = _
            New System.Timers.Timer(1000)

        AddHandler theTimer.Elapsed, _
            New System.Timers.ElapsedEventHandler( _
                AddressOf Timer_Elapsed)

        theTimer.Start()
    End Sub

    Private Sub Timer_Elapsed( _
           ByVal sender As Object, _
           ByVal e As System.Timers.ElapsedEventArgs)
        Invoke(New SetLabelDelegate( _
            AddressOf SetExecutingThreadLabel), _
         New Object() { _
          "Timers.Timer : " _
          & AppDomain.GetCurrentThreadId() & ": " _
          & InvokeRequired})

        CType(sender, System.Timers.Timer).Stop()
    End Sub

    Private Delegate Sub AsynchDelegate()

    Private Sub DelegateButton_Click(ByVal sender As System.Object, _
           ByVal e As System.EventArgs) Handles DelegateButton.Click
        Dim dlg As AsynchDelegate _
            = New AsynchDelegate(AddressOf AsynchExecuted)
        dlg.BeginInvoke(Nothing, Nothing)
    End Sub

    Private Sub AsynchExecuted()
        Invoke(New SetLabelDelegate( _
            AddressOf SetExecutingThreadLabel), _
        New Object() { _
          "Delegate : " _
          & AppDomain.GetCurrentThreadId() & ": " _
          & InvokeRequired})
    End Sub

Running the program and clicking on the buttons produces the results shown in Figures 7-27 through 7-29.

Output from Example 7-29 clicking Timer button

Figure 7-27. Output from Example 7-29 clicking Timer button

Output from Example 7-29 clicking Timers Timer button

Figure 7-28. Output from Example 7-29 clicking Timers Timer button

Output from Example 7-29 clicking Delegate button

Figure 7-29. Output from Example 7-29 clicking Delegate button

A few observations can be made from the above example and the related output:

  • The event executed by the System.Windows.Forms.Timer runs in the main thread. So it is perfectly safe to access the controls directly from within this Timer’s event handler. Be careful not to put any long-running code in this method. Otherwise, you will be holding up the main event-dispatch thread, and the performance and responsiveness of your application will suffer.

  • The System.Timers.Timer’s event handler executes in a thread from the thread pool. From this thread, you should not interact with the controls directly, as it is not the thread that owns them. You have to use the System.Windows.Forms.Control.Invoke() method.

  • The Delegate’s BeginInvoke() method calls the method from a thread pool thread as well. You should not access the controls directly from the invoked method either. Here, too, you have to call Invoke().

If you write an application that involves several threads, you can see how the above details can complicate your efforts. Further, forgetting them may lead to programs that misbehave. How can you ease these concerns?

One good way is to write a method that talks to the controls. Instead of accessing the controls from any random thread, call this method. It can easily check if it’s OK to access the controls directly, or if it should go through a delegate.

Let’s modify the code in Example 7-29 to illustrate this. From the three event-handler methods, call SetExecutingThreadLabel(). In this method, check to see if the executing thread is the one that owns the control. This can be done using the control’s InvokeRequired property.[3]

If the executing thread does own the control (InvokeRequired is false), then access it directly. Otherwise, SetExecutingThreadLabel() uses the Invoke() method to call itself, as shown in Example 7-31.

Example 7-31. Effectively addressing InvokeRequired issue

C# (ControlThread)

        private void SetExecutingThreadLabel(string message)
        {
            if(ExecutingThreadLabel.InvokeRequired)
            {
                Invoke(new SetLabelDelegate(SetExecutingThreadLabel),
                    new object[] {message});
            }
            else
            {
                ExecutingThreadLabel.Text = message;
            }
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            (sender as Timer).Stop();

            SetExecutingThreadLabel(
                    "Timer : "
                    + AppDomain.GetCurrentThreadId() + ": "
                    + InvokeRequired);
        }

        private void Timer_Elapsed(
            object sender, System.Timers.ElapsedEventArgs e)
        {
            (sender as System.Timers.Timer).Stop();

            SetExecutingThreadLabel(
                "Timer : "
                + AppDomain.GetCurrentThreadId() + ": "
                + InvokeRequired);
        }

        private void AsynchExecuted()
        {
            SetExecutingThreadLabel(
                "Delegate : "
                + AppDomain.GetCurrentThreadId() + ": "
                + InvokeRequired);
        }

VB.NET (ControlThread)

    Private Sub SetExecutingThreadLabel(ByVal message As String)
        If ExecutingThreadLabel.InvokeRequired Then
               Invoke(New SetLabelDelegate( _
                    AddressOf SetExecutingThreadLabel), _
                    New Object() {message})
        Else
            ExecutingThreadLabel.Text = message
        End If

    Private Sub Timer_Tick(ByVal sender As Object, _
           ByVal e As EventArgs)
        CType(sender, Timer).Stop()

        SetExecutingThreadLabel( _
          "Timer : " _
          & AppDomain.GetCurrentThreadId() & ": " _
          & InvokeRequired)
    End Sub

    Private Sub Timer_Elapsed( _
           ByVal sender As Object, _
           ByVal e As System.Timers.ElapsedEventArgs)
        CType(sender, System.Timers.Timer).Stop()

        SetExecutingThreadLabel( _
          "Timer : " _
          & AppDomain.GetCurrentThreadId() & ": " _
          & InvokeRequired)

    End Sub

    Private Sub AsynchExecuted()
        SetExecutingThreadLabel( _
          "Delegate : " _
          & AppDomain.GetCurrentThreadId() & ": " _
          & InvokeRequired)
    End Sub

As you can see from this example, the methods executed by the various threads don’t have to worry about Invoke(). They can simply call a method (SetExecutingThreadLabel() in this case) to communicate with the control, and that method determines if it has to use Invoke(). This approach not only makes it easier, it also helps deal with situations where you may inadvertently access controls from the wrong thread.

(The output screens are not shown here, because the change in the code does not affect them, except that the thread IDs are different.)

IN A NUTSHELL

Understand which thread is executing your code. This is critical to decide if you can communicate with your controls directly, or if you should use the System.Windows.Forms.Control.Invoke() method instead.

GOTCHA #63 Web-service proxy may fail when used for multiple asynchronous calls

Say you are interested in making multiple independent requests to a web service that has a method with a delayed response. You can certainly take advantage of the asynchronous access that the service proxy generated by wsdl.exe/Visual Studio provides. However, if you want to make more than one request at the same time, should you use the same proxy instance or different instances?

Taking a closer look at the BeginInvoke() and EndInvoke() methods of the proxy, it is clear that when you call BeginInvoke(), you are given an IAsyncResult handle. When you call EndInvoke(), you send this handle to it and get the result for that specific invocation. Doesn’t that suggest that you can make multiple asynchronous requests using a single instance of the proxy?

As it turns out, you can. However, you need to understand some issues from the web server’s point of view.

I have been asked this question quite a few times. I even ran into this situation inadvertently, and came to the realization illustrated in Examples 7-32 through 7-35.

Example 7-32. Multiple calls on a web service (C# server side)

C# (MultipleWSCalls), server side

//MyService.asmx.cs part of ACSWSForMultiRequest.dll (Web Service)
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;

namespace ACSWSForMultiRequest
{
    [WebService(Namespace="http://www.ACSWSForMultiRequest.com")]
    public class MyService : System.Web.Services.WebService
    {
        /// ...

        [WebMethod]
        public string Method1(int val)
        {
            System.Threading.Thread.Sleep(5000);
            return val.ToString();
        }
    }
}

Example 7-33. Multiple calls on a web service (C# client side)

C# (MultipleWSCalls), client side

///Test.cs part of AClient.exe
using System;
using AClient.ACSWSForMultiRequest;

namespace AClient
{
    class Test
    {
        [STAThread]
        static void Main(string[] args)
        {
            MyService service = new MyService();
            service.Credentials =
                System.Net.CredentialCache.DefaultCredentials;

            Console.WriteLine(
                "Making synchronous request at {0}",
                DateTime.Now.ToLongTimeString());

            Console.WriteLine("Recevied {0} at {1}",
                service.Method1(0),
                DateTime.Now.ToLongTimeString());

            Console.WriteLine("Making two requests at {0}",
                DateTime.Now.ToLongTimeString());

            service.BeginMethod1(1, new AsyncCallback(display), service);

            service.BeginMethod1(2, new AsyncCallback(display),
                service);

            Console.ReadLine();
        }

        private static void display(IAsyncResult handle)
        {
            MyService theService =
                handle.AsyncState as MyService;

            string result = theService.EndMethod1(handle);

            Console.WriteLine("Result {0} received {1}",
                result, DateTime.Now.ToLongTimeString());
        }
    }
}

Example 7-34. Multiple calls on a web service (VB.NET server side)

VB.NET (MultipleWSCalls), server side

'MyService.asmx.vb part of AVBWSForMultiRequest.dll (Web Service)
Imports System.Web.Services

<System.Web.Services.WebService(Namespace := _
   "http://tempuri.org/AVBWSForMultiRequest/MyService")> _
Public Class MyService
    Inherits System.Web.Services.WebService

        '...

    <WebMethod()> _
    Public Function Method1(ByVal val As Integer) As String
        System.Threading.Thread.Sleep(5000)
        Return val.ToString()
    End Function
End Class

Example 7-35. Multiple calls on a web service (VB.NET client side)

VB.NET (MultipleWSCalls), client side

''Test.vb part of AClient.exe
Imports AClient.AVSWSForMultiRequest

Module Test

    Sub Main()
        Dim service As New MyService
        service.Credentials = _
         System.Net.CredentialCache.DefaultCredentials

        Console.WriteLine( _
           "Making synchronous request at {0}", _
           DateTime.Now.ToLongTimeString())

        Console.WriteLine("Recevied {0} at {1}", _
         service.Method1(0), _
         DateTime.Now.ToLongTimeString())

        Console.WriteLine("Making two requests at {0}", _
         DateTime.Now.ToLongTimeString())

        service.BeginMethod1(1, _
            New AsyncCallback(AddressOf display), service)

        service.BeginMethod1(2, _
            New AsyncCallback(AddressOf display), service)

        Console.ReadLine()
    End Sub

    Private Sub display(ByVal handle As IAsyncResult)
        Dim theService As MyService = _
         CType(handle.AsyncState, MyService)

        Dim result As String = theService.EndMethod1(handle)

        Console.WriteLine("Result {0} received {1}", _
         result, DateTime.Now.ToLongTimeString())
    End Sub

End Module

In this example, the web service has a method Method1() that has an artificial delay to simulate some activity. The client first makes one synchronous call, to which it gets a response after five seconds. (The reason you see an additional two-second delay on the first call is that the web service warms up when it receives the first request.) Then the client makes two asynchronous calls, one right after the other. The output from the above program is shown in Figure 7-30.

Output from Example 7-32

Figure 7-30. Output from Example 7-32

No problem—so far. Now, let’s say the service maintains state and expects the client code to set the proxy’s CookieContainer property, as shown in Example 7-36.

Example 7-36. Making multiple calls when session is maintained

C# (MultipleWSCalls )

//MyService.asmx.cs part of ACSWSForMultiRequest.dll (Web Service)

...
        [WebMethod(true)]
        public string Method1(int val)
        {
            System.Threading.Thread.Sleep(5000);
            return val.ToString();
        }
...


//Test.cs. part of AClient.exe

...

            MyService service = new MyService();
            service.CookieContainer =
                new System.Net.CookieContainer();

            service.Credentials =
                System.Net.CredentialCache.DefaultCredentials;

...

VB.NET (MultipleWSCalls)

'MyService.asmx.vb part of AVBWSForMultiRequest.dll (Web Service)
...
    <WebMethod(True)> _
    Public Function Method1(ByVal val As Integer) As String
        System.Threading.Thread.Sleep(5000)
        Return val.ToString()
    End Function
...


'Test.vb part of AClient.exe

...

        Dim service As New MyService
        service.CookieContainer = _
         New System.Net.CookieContainer

        service.Credentials = _
         System.Net.CredentialCache.DefaultCredentials

...

In this code you have made one change to the web service. In the WebMethod attribute, you have set the parameter (true) to enable session management. You have also set the CookieContainer property on the client proxy to a non-null reference. Let’s run the application now and see what the response is. The output is shown in Figure 7-31.

Output from Example 7-36

Figure 7-31. Output from Example 7-36

Even though the two asynchronous requests are made simultaneously, the responses are separated by a five-second delay. This is because on the server side the requests get serialized. The web service recognizes the session id from the cookie that the proxy transmits and determines that the two requests belong to the same session. As a result, in order to avoid any threading conflicts, it serializes the requests, allowing only one request belonging to a session to execute at a time.

IN A NUTSHELL

When you send multiple concurrent requests to a web service, do not use the same proxy instance if you set the CookieContainer property on the proxy, or if session state is supported by the web service. Doing so causes the requests to execute consecutively on the server, rather than concurrently. If consecutive behavior is what you want, then this is a good approach.

GOTCHA #64 Raising events lacks thread-safety

When you are about to raise an event, what happens if all handlers for that event are suddenly removed by other threads? What you don’t want is for your program to crash with a NullReferenceException, which it may well do if you aren’t careful. In Gotcha #7, "Uninitialized event handlers aren’t treated gracefully,” I discussed the issues with raising events. At the end of that gotcha I raised a concern about thread safety. In this gotcha I address that.

Unfortunately, this problem manifests itself slightly differently in C# than it does in VB.NET. First, I’ll discuss the problem with raising events in VB.NET. Then I’ll discuss the same problem in C#. The discussion regarding VB.NET is relevant for C# programmers as I build on it.

First, RaiseEvent() in VB.NET is not thread-safe. Consider the example in Example 7-37.

Example 7-37. Example of RaiseEvent in VB.NET

VB.NET (RaisingEventThreadSafety)

Imports System.Threading

Public Class AComponent
    Public Event myEvent As EventHandler

    Private Sub DoWork()
        Thread.Sleep(5) ' Simulate some work delay
        RaiseEvent myEvent(Me, New EventArgs)
    End Sub
    Public Sub Work()
        Dim aThread As New Thread(AddressOf DoWork)
        aThread.Start()
    End Sub
End Class

'Test.vb
Imports System.Threading

Module Test

    Private Sub TestHandler(ByVal sender As Object, ByVal e As EventArgs)
        Console.WriteLine("TestHandler called")
    End Sub

    Sub Main()
        Dim obj As New AComponent

        AddHandler obj.myEvent, AddressOf TestHandler

        obj.Work()

        Thread.Sleep(5)

        RemoveHandler obj.myEvent, AddressOf TestHandler
    End Sub

End Module

In the Work() method of the AComponent class, you call the DoWork() method in a separate thread. In the DoWork() method you raise an event after a small delay (5ms). In the Main() method of the Test class you create an instance of AComponent, register a handler with it, then call its Work() method. You then quickly (after a delay of 5 ms) remove the handler you registered. A quick run of this program produces the output in Figure 7-32.

Output from AComponent.vb

Figure 7-32. Output from AComponent.vb

It worked this time, but you just got lucky—lucky that the thread executing DoWork() got to its RaiseEvent() before Main() got to its RemoveHandler(). With the code the way it is, though, there’s no guarantee things will always turn out so well.

There’s good news and there’s bad news. First, let’s look at the good news. AddHandler and RemoveHandler are thread-safe. The VB.NET compiler translates them into calls to special hidden thread-safe add and remove methods (add_myEvent() and remove_myEvent() in this case). You can verify this by observing the MSIL code for Example 7-37 in Figure 7-33.

Notice that AddHandler() is translated to a call to the add_myEvent() method. add_myEvent()is marked synchronized (as you can see at the top of Figure 7-34). What does that mean? When add_myEvent() is called, it gains a lock on the AComponent instance that myEvent belongs to. Within the method, the registration of the handler (by the call to the Combine() method on the delegate) is done with thread safety. The code for remove_myEvent() method is similarly thread-safe. You can see this by examining the MSIL (not shown here) using ildasm.exe.

MSIL for AddHandler and RemoveHandler calls in Example 7-37

Figure 7-33. MSIL for AddHandler and RemoveHandler calls in Example 7-37

Auto-generated hidden special thread-safe add_myEvent() method

Figure 7-34. Auto-generated hidden special thread-safe add_myEvent() method

Unfortunately, RaiseEvent is not thread-safe, as you can see from the MSIL in Figure 7-35.

MSIL for RaiseEvent that shows lack of thread-safety

Figure 7-35. MSIL for RaiseEvent that shows lack of thread-safety

The event field is first loaded and verified to be not Nothing. If the test succeeds, then the field is loaded again, and the Invoke() method is called. Here is the gotcha: what if the field is set to null/Nothing between the check and the reload of the field? Tough luck. Can this happen? You bet it can.

I wanted to go to extreme measures to prove to myself that this is true. So, I got into the Visual Studio debugger, asked for Disassembly, and started stepping through the assembly. By doing so, I slowed down the execution of the RaiseEvent statement, allowing the Main() method to modify the event field before RaiseEvent completes. I had no trouble producing the NullReferenceException shown in Figure 7-36

RaiseEvent failing due to lack of thread-safety

Figure 7-36. RaiseEvent failing due to lack of thread-safety

How can you avoid this problem? There is no good answer. The simplest solution that comes to mind is to surround the RaiseEvent() call with a SyncLock block:

    SyncLock Me
        RaiseEvent myEvent(Me, New EventArgs)
    End SyncLock

While this certainly provides thread safety from concurrent AddHandler and RemoveHandler calls, it may also delay the registration and unregistration of handlers if the event-handler list is large. It would be nice if you could get the list of delegates from the event. Unfortunately, the VB.NET compiler does not allow you to access the underlying delegate (the C# compiler does).

One possibility is to use delegates directly instead of using an event. This, however, introduces some complications as well. Unlike registering and unregistering for events, registering and unregistering handlers for delegates is not thread-safe; you have to deal with thread safety yourself if you go this route.

Let’s look at how this is similar and how this differs in C#. In C#, you use the += operator to register a handler for an event, and -= to unregister. These two calls become calls to the hidden special add and remove methods, as discussed above for VB.NET. Providing thread safety while raising events in C# is pretty easy. The code in Example 7-38 shows how to do that.

Example 7-38. C# example to raise event with thread-safety

C# (RaisingEventThreadSafety)

//AComponent.cs

using System;
using System.Threading;

namespace RaisingEvent
{
    public class AComponent
    {
        public event EventHandler myEvent;

        private void DoWork()
        {
            EventHandler localHandler = null;
            lock(this)
            {
                if (myEvent != null)
                {
                    Console.WriteLine("myEvent is not null");
                    Thread.Sleep(2000);
                    // Intentional delay to illustrate

                    localHandler = myEvent;
                    Console.WriteLine("Got a safe copy");
                }
            }

            if (localHandler != null)
            {
                localHandler(this, new EventArgs());
            }
            else
            {
                Console.WriteLine("localHandler is null!!!!!!!!!!!");
            }
        }

        public void Work()
        {
            Thread aThread = new Thread(
                new ThreadStart(DoWork));

            aThread.Start();
        }
    }
}

//Test.cs

using System;
using System.Threading;

namespace RaisingEvent
{
    public class Test
    {
        private static void TestHandler(
            object sender, EventArgs e)
        {
            Console.WriteLine("Handler called");
        }

        public static void TestIt()
        {
            AComponent obj = new AComponent();
            obj.myEvent += new EventHandler(TestHandler);

            obj.Work();

            Thread.Sleep(1000);

            Console.WriteLine("Trying to unregister handler");
            obj.myEvent -= new EventHandler(TestHandler);
            Console.WriteLine("handler unregistered");
        }

        public static void Main()
        {
            TestIt();
        }
    }
}

The output from this example is shown in Figure 7-37. You can see that the remove-handler code (the -= operator) in the TestIt() method blocks while a copy of the reference to myEvent is made in DoWork().

Output from Example 7-38 illustrates thread-safety

Figure 7-37. Output from Example 7-38 illustrates thread-safety

Well, that looks good. Are you done? I wish. For its share, C# has made a mess with the thread safety of events when the registration and unregistration of handlers is done inside the class owning the event. But first, let’s quickly take a look at the MSIL for the TestIt() method as shown in Figure 7-38.

MSIL for the Test.TestIt() method in Example 7-38

Figure 7-38. MSIL for the Test.TestIt() method in Example 7-38

As you can see, the call to += in the source code of Testit() method uses the thread-safe add_myEvent() method at the MSIL level. This is good news.

Now, copy the TestIt() method and the TestHandler() method from the Test class to the AComponent class, compile the code and take a look at the MSIL (shown in Figure 7-39).

MSIL for AComponent.Testit() method

Figure 7-39. MSIL for AComponent.Testit() method

Unfortunately, this version uses the Combine() method directly instead of the thread-safe add_myEvent() method. Let’s make a small change to Main() as shown below. The output after this change is shown in Figure 7-40.

Output after the above change to Main()

Figure 7-40. Output after the above change to Main()

        public static void Main()
        {
            Console.WriteLine("---- Calling Main.TestIt ----");
            TestIt();
            Thread.Sleep(5000);
            Console.WriteLine("---- Calling AComponent.TestIt ----");
            AComponent.TestIt();
        }

You would expect the result of the calls to Test.TestIt() and AComponent.TestIt() to be identical, but they’re not. This is because event registration and unregistration are not thread-safe within the class that contains the event.

While this gotcha is a problem in C#, it does not exist in VB.NET. AddHandler() and RemoveHandler() are consistently thread-safe no matter where they’re called from.

IN A NUTSHELL

  • Calls to register and unregister events are thread-safe in VB.NET.

  • In C#, they are thread-safe only if called from outside the class with the event. For calls within the class, you must provide thread safety yourself by calling += or -= within a lock(this) statement.

  • The thread-safe calls to register and unregister events rely on the special hidden thread-safe add and remove methods, respectively.

  • Calls to RaiseEvent in VB.NET are inherently not thread-safe. You need to take care of thread-safety for these calls.

  • You can use the underlying delegate to raise the event in a thread-safe manner in C#. You achieve this by getting a local reference to the delegate within a lock(this) statement and then using the local reference (if not null) to raise the event.



[3] Thanks to Naresh Chaudhary for refactoring this code.

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

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