Debugging Multithreaded Applications as Singlethreaded in C#

5 min read

We can identify threads created using both the BackgroundWorker component and the Thread class. We can also identify the main application thread and we learned about the information shown by the Threads window. However, we must debug the encryption process to solve its problem without taking into account the other concurrent threads. How can we successfully debug the encryption engine focusing on one thread and leaving the others untouched?

We can use the Threads window to control the execution of the concurrent thread at runtime without having to make changes to the code. This will affect the performance results, but it will allow us to focus on a specific part of the code as if we were working in a single-threaded application.

This technique is suitable for solving problems related to a specific part of the code that runs in a thread. However, when there are problems generated by concurrency we must use other debugging tricks that we will be learning shortly.

The Threads window does a great job in offering good runtime information about the running threads while offering a simple way to watch, pause, and resume multiple threads.

Time for action – Leaving a thread running alone

You must run the encryption procedure called by ThreadEncryptProcedure. But you want to focus on just one thread, in order to solve the problem that the FBI agents detected. Changing the code is not an option, because it will take more time than expected, and you might introduce new bugs to the encryption engine. Thus, let’s freeze the threads we are not interested in!

Now, we are going to leave one encryption thread running alone to focus on its code without the other threads disturbing our debugging procedure:

  1. Stay in the project, SMSEncryption.
  2. Clear all the breakpoints. Press Ctrl + Shift + F9 or select Debug | Delete AllBreakpoints in the main menu. Make sure the Threads window is visible.
  3. Define a breakpoint in the line int liThreadNumber = (int)poThreadParameter; in the ThreadEncryptProcedure procedure code.
  4. Enter or copy and paste a long text, using the same lines (with more than 30,000 lines) in the Textbox labeled Original SMS Messages, as shown in the following image:

    Debugging Multithreaded Applications as Singlethreaded in C#

  5. Click on the Run in a thread button. The line with the breakpoint defined in the ThreadEncryptProcedure procedure is shown highlighted as the next statement that will be executed. The current thread will be shown with a yellow arrow on the left in the Threads window.
  6. Right-click on each of the other encryption threads and select Freeze in the context menu that appears, in order to suspend them. If the current thread is Encryption #1 and there are four cores available, you will freeze the following threads—Encryption #0, Encryption #2, and Encryption #3.
  7. Right-click on the Main thread and select Freeze in the context menu that appears, in order to suspend it (we do not want the BackgroundWorker to start and interfere with our work). The only working thread that matters will be Encryption #1, as shown in the following image:

    Debugging Multithreaded Applications as Singlethreaded in C#

  8. Run the code step-by-step inspecting values as you do with single-threaded applications.

What just happened?

It is easy to debug a multi hreaded application focusing on one thread instead of trying to do it with all the threads running at the same time.

We could transform a complex multi threaded application into a single-threaded application without making changes to the code. We did it at runtime using the multithreading debugging features offered by the C# IDE.

We suspended the execution of the concurrent threads that would disturb our step-by-step execution. Thus, we could focus on the code being executed by just one encryption thread.

Freezing and thawing threads

Freezing a thread suspends its execution. However, in the debugging process, we would need to resume the thread execution.

It can be done at any point of ti me by right-clicking on a suspended thread and selecting Thaw in the context menu that appears, as shown in the following image:

Debugging Multithreaded Applications as Singlethreaded in C#

By Freezing and Thawing threads (suspending and resuming), we can have an exhaustive control over the threads running during the debugging process. It helps a lot when we have to solve bugs related to concurrency as we can easily analyze many contexts without making changes to the code—which could generate new bugs.

Nevertheless, when developing multithreaded applications, we must always test the execution with many concurrent threads running to make sure it does not have concurrency bugs.

The debugging techniques allow us to isolate the code for evaluation purposes, but the final tests must use the full multithreading potential.

Viewing the call stack for each running thread

Each thread has its own independent stack. Using the Call Stack window, we can move through the methods that were called, as we are used to doing so in single-threaded applications.

The main difference in doing this with multithreaded applications is that when the active thread changes, the Call Stack window will also show different content.

Debugging a multi hreaded application using the techniques we are learning is an excellent way to understand how the different threads run and will improve our parallel programming skills.

To show the call stack for the active thread, press Ctrl + Alt + C or go to Debug | Windows | Call Stack in the main menu. Make sure the Threads window is also visible to take into account the active thread when analyzing the call stack, as shown in the following image:

Debugging Multithreaded Applications as Singlethreaded in C#

Have a go hero – Debugging and enhancing the encryption algorithm

Using the multithreaded debugging techniques we have learned so far, develop a new version of this application with the encryption problem solved. Take into account everything we have studied about freezing and thawing threads.

Check the randomly generated garbage and the way it is applied to the generated encrypted string. Making some changes to it, you can have a robust encryption process that differentiates each output with the same input text.

You can improve the new versions by using new randomly generated garbage to enhance the encryption algorithms.

Oh no! You have to explain to the agents the changes you made to the encryption procedure, and how it works.



Please enter your comment!
Please enter your name here