Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Parallel Programming and Concurrency with C# 10 and .NET 6

You're reading from   Parallel Programming and Concurrency with C# 10 and .NET 6 A modern approach to building faster, more responsive, and asynchronous .NET applications using C#

Arrow left icon
Product type Paperback
Published in Aug 2022
Publisher Packt
ISBN-13 9781803243672
Length 320 pages
Edition 1st Edition
Languages
Tools
Concepts
Arrow right icon
Author (1):
Arrow left icon
Alvin Ashcraft Alvin Ashcraft
Author Profile Icon Alvin Ashcraft
Alvin Ashcraft
Arrow right icon
View More author details
Toc

Table of Contents (18) Chapters Close

Preface 1. Part 1:Introduction to Threading in .NET
2. Chapter 1: Managed Threading Concepts FREE CHAPTER 3. Chapter 2: Evolution of Multithreaded Programming in .NET 4. Chapter 3: Best Practices for Managed Threading 5. Chapter 4: User Interface Responsiveness and Threading 6. Part 2: Parallel Programming and Concurrency with C#
7. Chapter 5: Asynchronous Programming with C# 8. Chapter 6: Parallel Programming Concepts 9. Chapter 7: Task Parallel Library (TPL) and Dataflow 10. Chapter 8: Parallel Data Structures and Parallel LINQ 11. Chapter 9: Working with Concurrent Collections in .NET 12. Part 3: Advanced Concurrency Concepts
13. Chapter 10: Debugging Multithreaded Applications with Visual Studio 14. Chapter 11: Canceling Asynchronous Work 15. Chapter 12: Unit Testing Async, Concurrent, and Parallel Code 16. Assessments 17. Other Books You May Enjoy

Creating and destroying threads

Creating and destroying threads are fundamental concepts of managed threading in .NET. We have already seen one code example that created a thread, but there are some additional constructors of the Thread class that should be discussed first. Also, we will look at a few methods of pausing or interrupting thread execution. Finally, we will cover some ways to destroy or terminate a thread’s execution.

Let’s get started by going into more detail regarding creating and starting threads.

Creating managed threads

Creating managed threads in .NET is accomplished by instantiating a new Thread object. The Thread class has four constructor overloads:

  • Thread(ParameterizedThreadStart): This creates a new Thread object. It does this by passing a delegate with a constructor that takes an object as its parameter that can be passed when calling Thread.Start().
  • Thread(ThreadStart): This creates a new Thread object that will execute the method to be invoked, which is provided as the ThreadStart property.
  • Thread(ParameterizedThreadStart, Int32): This adds a maxStackSize parameter. Avoid using this overload because it is best to allow .NET to manage the stack size.
  • Thread(ThreadStart, Int32): This adds a maxStackSize parameter. Avoid using this overload because it is best to allow .NET to manage the stack size.

Our first example used the Thread(ThreadStart) constructor. Let’s look at a version of that code that uses ParameterizedThreadStart to pass a value by limiting the number of iterations of the while loop:

Console.WriteLine("Hello, World!");
var bgThread = new Thread((object? data) =>
{
    if (data is null) return;
    int counter = 0;
    var result = int.TryParse(data.ToString(), 
        out int maxCount);
    if (!result) return;
    while (counter < maxCount)
    {
        bool isNetworkUp = System.Net.NetworkInformation
            .NetworkInterface.GetIsNetworkAvailable();
        Console.WriteLine($"Is network available? Answer: 
            {isNetworkUp}");
        Thread.Sleep(100);
        counter++;
    }
});
bgThread.IsBackground = true;
bgThread.Start(12);
for (int i = 0; i < 10; i++)
{
    Console.WriteLine("Main thread working...");
    Task.Delay(500);
}
Console.WriteLine("Done");
Console.ReadKey();

If you run the application, it will run just like the last example, but the background thread should only output 12 lines to the console. You can try passing different integer values into the Start method to see how that impacts the console output.

If you want to get a reference to the thread that is executing the current code, you can use the Thread.CurrentThread static property:

var currentThread = System.Threading.Thread.CurrentThread;

This can be useful if your code needs to check the current thread’s ManagedThreadId, Priority, or whether it is running in the background.

Next, let’s look at how we can pause or interrupt the execution of a thread.

Pausing thread execution

Sometimes, it is necessary to pause the execution of a thread. A common real-life example of this is a retry mechanism on a background thread. If you have a method that sends log data to a network resource, but the network is unavailable, you can call Thread.Sleep to wait for a specific interval before trying again. Thread.Sleep is a static method that will block the current thread for the number of milliseconds specified. It is not possible to call Thread.Sleep on a thread other than the current one.

We have already used Thread.Sleep in the examples in this chapter, but let’s change the code slightly to see how it can impact the order of events. Change the Thread.Sleep interval inside the thread to 10, remove the code that makes it a background thread, and change the Task.Delay() call to Thread.Sleep(100):

Console.WriteLine("Hello, World!");
var bgThread = new Thread((object? data) =>
{
    if (data is null) return;
    int counter = 0;
    var result = int.TryParse(data.ToString(), out int 
        maxCount);
    if (!result) return;
    while (counter < maxCount)
    {
        bool isNetworkUp = System.Net.NetworkInformation.
            NetworkInterface.GetIsNetworkAvailable();
        Console.WriteLine($"Is network available? Answer: 
             {isNetworkUp}");
        Thread.Sleep(10);
        counter++;
    }
});
bgThread.Start(12);
for (int i = 0; i < 12; i++)
{
    Console.WriteLine("Main thread working...");
    Thread.Sleep(100);
}
Console.WriteLine("Done");
Console.ReadKey();

When running the application again, you can see that putting a greater delay on the primary thread allows the process inside bgThread to begin executing before the primary thread completes its work:

Figure 1.2 – Using Thread.Sleep to change the order of events

Figure 1.2 – Using Thread.Sleep to change the order of events

The two Thread.Sleep intervals can be adjusted to see how they impact the console output. Give it a try!

Additionally, it is possible to pass Timeout.Infinite to Thread.Sleep. This will cause the thread to pause until it is interrupted or aborted by another thread or the managed environment. Interrupting a blocked or paused thread is accomplished by calling Thread.Interrupt. When a thread is interrupted, it will receive a ThreadInterruptedException exception.

The exception handler should allow the thread to continue working or clean up any remaining work. If the exception is unhandled, the runtime will catch the exception and stop the thread. Calling Thread.Interrupt on a running thread will have no effect until that thread has been blocked.

Now that you understand how to create an interrupt thread, let’s wrap up this section by learning how to destroy a thread.

Destroying managed threads

Generally, destroying a managed thread is considered an unsafe practice. That is why .NET 6 no longer supports the Thread.Abort method. In .NET Framework, calling Thread.Abort on a thread would raise a ThreadAbortedException exception and stop the thread from running. Aborting threads was not made available in .NET Core or any of the newer versions of .NET. If some code needs to be forcibly stopped, it is recommended that you run it in a separate process from your other code and use Process.Kill to terminate the other process.

Any other thread termination should be handled cooperatively using cancelation. We will see how to do this in the Scheduling and canceling work section. Next, let’s discuss some of the exceptions to handle when working with managed threads.

You have been reading a chapter from
Parallel Programming and Concurrency with C# 10 and .NET 6
Published in: Aug 2022
Publisher: Packt
ISBN-13: 9781803243672
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime