Debugging Multithreaded Applications

A multithreaded application is one in which more than a single thread is running in a given process. By default, each process that runs your application has at least one thread of execution. You might create multiple threads to do parallel processing. This can significantly improve performance, especially when run on today’s multicore processors and hyperthreading technology. However, multithreading comes at a cost. The code can be more complex to write and more difficult to debug. If you’ve ever written a multithreaded application, you already know this. For example, just stepping line by line through a multithreaded application to debug it might have you jumping from one thread to another. You would then have to keep track of this flow in your head to make sense of the diagnostic information you see.

Fortunately, Visual Studio provides a few tools that make the job a bit easier. We do not cover coding a multithreaded application here. Instead, we cover the debug options available to you for debugging one, such as the following:

Image The ability to view threads in your source during a debug session

Image The Debug Location toolbar used to view processes, threads, and flagged threads

Image The Thread window used to work with a list of threads in your application

Image Breakpoint filters that enable you to set a breakpoint for an individual thread

Let’s look at each of these features in more detail.


Note

MSDN provides a simple code sample that is useful for working through debugging a multithreaded application. Search for the topic “Walkthrough: Debugging a Multithreaded Application.” We use that code sample here to help drive home the key debugging concepts. You can also download this sample from the book’s website.


Discovering and Flagging Threads

Visual Studio enables you to visualize the threads in your application in debug mode. When you are stopped on a breakpoint, your application is paused, and all threads in that application are halted. The threads are still there. They are put in a suspended state so you can examine their statuses. They do not continue until you continue the execution of your code. However, in a multithreaded scenario, threads outside the one on which your code broke might not be easily visible in the debugger. To see them in the Debug menu, you can use the Show Threads in Source option from the Debug menu, as shown in Figure 10.43.

Image

FIGURE 10.43 Select the Show Threads in Source icon from the Debug toolbar to tell Visual Studio to visually display threads in the debug session.

Selecting Show Threads in Source highlights other threads that exist in your code in the indicator margin (or gutter) of the code window during a debug session. The icon used to highlight these items looks like two wavy lines (or cloth threads). Figure 10.44 shows an example of a multithreaded application in a debug session.

Image

FIGURE 10.44 The thread icon in the indicator margin of the code window indicates that a thread is stopped on a line of code in the debug session.


Note

Most debug scenarios are single threaded. Therefore, you will not see another thread executing. You must write an application that uses more than one thread to see the thread icon in the debugger.


Notice the graphic on the left of line 26. This indicates that a thread exists at this location in your source code. Hovering over the indicator shows the thread or threads that the indicator references. Each thread is shown by its ID number (in brackets) and name (if any).


Tip

Naming threads can help you better identify them when debugging. To name a thread, you set the value of the Name string property of the System.Threading.Thread instance you are interested in. Also, notice that you can’t rename the same thread more than once or a System.InvalidOperationException is thrown.


Now that you’ve found a thread, you might want to flag it for further monitoring. This simply helps group it with the threads you want to monitor versus those you do not care about. You can flag a thread right from the indicator margin. To do so, right-click the indicator and choose the Flag option on the context menu. You can see this flag under the cursor shown in Figure 10.44.

Flagged threads show up highlighted (red flag) in the Threads window (Debug, Windows, Threads). This window is shown at the bottom of Figure 10.44. You can use this window to flag or unflag additional threads. Flagged threads provide special grouping in both the Thread window and the Debug Location toolbar. We cover these features next.

Managing Debug Processes and Threads

You can switch between the processes you are debugging and the threads within those processes by using the Debug Location toolbar. (You might have to right-click the toolbar area and add this toolbar to the IDE.) This toolbar is shown in Figure 10.45. On the left is the Process list. Here you can select a process to view details about that process, including executing threads. Many multithreaded applications are run within a single process, however.

Image

FIGURE 10.45 The Debug Location toolbar.

The Thread list drop-down (see Figure 10.45) on the Debug Location toolbar shows a list of threads for the selected process. Notice that the threads are shown with their IDs, names, and flag indicators. You can select a thread in this list to jump to source code associated with the thread. If no source code is associated with a selected thread, the IDE indicates that source code is not available.

You can filter the list to show only flagged threads by toggling the second button to the right of the Thread list (shown with two flags). The first button to the right flags (or unflags) the current, active thread.

You can also manage threads from within the Threads window (Debug, Windows, Threads). Here you see all threads listed for a given process. Figure 10.46 shows an example. Notice that the left of the list shows the flagged status of threads. We have flagged a thread in a sample application. Notice also that these threads can be named. This allows for easy recognition in the Name column.

Image

FIGURE 10.46 The Threads window provides total control over the threads in a debug session.

You have several options available when you right-click a thread in the window, as shown in the context menu in Figure 10.46. Notice the Switch to Thread option, which allows you to switch the active thread being debugged. The active thread is shown with a yellow arrow in the thread list (to the right of the flag). Switching active threads changes the debug context and content in the debug windows. You can also Freeze (or pause) threads using this context menu. (Of course, you can then thaw [or resume] them, too.) Freezing a thread is equivalent to suspending it. The icons for freezing and thawing selected threads are on the far right side of the toolbar in the Threads window. The Threads window has a number of other features. You can use it to search the calls stack group threads by process, ID, category, priority, suspended state, and more.


Tip

When debugging multithreaded applications, it’s often easier to freeze all but one thread. This allows you to focus on what is happening with the given thread.


Inspecting Individual Threads

When your application hits a breakpoint, all executing threads are paused. This enables you to inspect them individually. As you’ve seen, you can use the Debug Locations toolbar (refer to Figure 10.45) to change the selected thread in the IDE. Doing so reconfigures the debug windows. This includes the call stack and Watch windows (including Autos and Locals).

For example, imagine you are working on an application that has a main thread of execution. It might then create two additional threads on which it does work. In fact, the sample we started this section with works just like this. We added two lines of code to the sample application (which you can download): one to name each of the threads (InstanceCaller thread and StaticCaller thread). We then increased the sleep times in each of the methods used as delegates to create and sleep the threads. Finally, we set a breakpoint in the StaticMethod on the Thread.Sleep line of code.

When you break into the application, you can inspect each of the executing threads. Here you can see the code state for the selected thread (or any of the other threads spawned by the application). Figure 10.47 shows an example. Notice that the StaticCaller thread is the active thread in the Debug Locations toolbar (top of the drop-down list). The code is also stopped inside this method.

Image

FIGURE 10.47 You can switch to active threads using the Debug Location toolbar. Name your threads to make them easier to work with.

The sample code actually creates the InstanceCaller thread first and puts it to sleep to simulate a long-running operation. You can select this thread using the drop-down list shown in Figure 10.47. Figure 10.48 shows this thread selected. Notice that the code window changes to the current line being executed by this thread (Thread.Sleep). Because this is not the active thread, this icon in the indicator margin is a different type of arrow, and the selection is a different color (by default). In addition, the Autos window now shows values for this thread, and the call stack shows the lifeline of this code.

Image

FIGURE 10.48 Selecting another thread in a debug session allows you to view the call stack for that thread and inspect its variables.

Breaking Based on a Specific Thread

You can also break on a line of code when it hits a thread. To do so, set a breakpoint in your code and choose a breakpoint filter (covered earlier). Figure 10.49 shows an example. In this example, the filter is set based on the thread name. You could have multiple threads calling into this method. However, you would only hit the breakpoint when the specific thread hit this method.

Image

FIGURE 10.49 You can add a breakpoint filter to stop Visual Studio on a specific thread.

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

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