Threads in C#: What are They and When to Use Them?

Jan 3, 2024 | C#

Programming, like reality, requires multitasking. In our world, threads are the secret heroes making that possible. They are the essential building blocks which allow our software to perform multiple tasks simultaneously. Intrigued? Let’s dive into this adventure of thread understanding!.

Understanding Threads in C#

Buckle up! We’re about to embark on a journey through the core of threading in C#. We’ll aim to unravel the mystery behind what threads are, how straightforward they actually can be, and just how many threads one can generate.

What are Threads in C#

In simple terms, threads are the smallest sequences of programmed instructions that can be managed individually by a scheduler. Eager to see how this works? Let’s dive into some code.

using System.Threading;

public class Program
{
    public static void Main()
    {
        Thread thread = new Thread(new ThreadStart(Work));
        thread.Start();  // Starts the thread.

        for (int i = 0; i < 100; i++) Console.Write('Main');
    }

    public static void Work()
    {
        for (int i = 0; i < 100; i++) Console.Write('Other');
    }
}

In this code snippet, we create a new thread thread and direct it to a method Work(). As soon as we initiate thread.Start(), it runs alongside the Main() method integrated into the primary thread. Hence, both methods execute simultaneously, demonstrating the power of threading in action!

The Basics of C# Threading

Threading in C# is all about executing multiple processes or tasks at the same time. But are you ever struck by the question – Is it genuinely possible to run numerous tasks simultaneously? Let’s explore.

The truth is, a single-core CPU cannot execute multiple tasks at precisely the same time. It rather executes one task, switches to another task, performs a bit of that, and then goes back to the previous job. This process is known as context switching, and the illusion of parallelism born from it is referred to as concurrency.

Now, keep in mind that modern CPUs come with multiple cores that are capable of performing tasks in parallel – achieving true multitasking. Now the big question is, how many threads can we actually create?

How Many Threads Can I Create in C#

The number of threads one can create isn’t infinite. It largely depends on the memory available. Each thread consumes some amount of memory, so when memory runs out, no more threads can be created. To do a quick check on number of threads, you could use the following:

int workerThreads;
int completionPortThreads;
ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
Console.WriteLine("Max worker threads: " + workerThreads 
                  + ", Max completion port threads: " + completionPortThreads);

This piece of code gives us the maximum number of ThreadPool threads the system can create. An important thing to remember – creating too many threads can cause ‘thrashing’, and that’s never a good thing!

Delving Deeper Into Threads and Threading in C#

We’ve just skimmed the surface. Let’s now dive deeper to understand some prime features of threading, like C# Threading Sleep, handling multiple threads and getting our hands dirty with debugging. Up for the challenge?

Exploring C# Threading Sleep

In C#, Thread.Sleep() is commonly employed to pause the execution of the current thread for a specified period. Got an important appointment but your dish is still cooking? That’s exactly when you would use Thread.Sleep(). Here’s a glimpse:

using System.Threading;

class Geeks {
    static public void Main()
    {
        Console.WriteLine("Before Sleep");

        // Pausing program for 3 seconds
        Thread.Sleep(3000);

        Console.WriteLine("After Sleep");
    }
}

In the snippet above, the Thread.Sleep(3000); line forces the program to sleep for 3 seconds (3000 milliseconds) between printing “Before Sleep” and “After Sleep”. Isn’t this a handy way to control the execution flow?

Implementing Multiple Threading in C#

Running multiple threads in C# is quite straightforward. Shall we dive into a little example to see it in action?

// Creating threads 
Thread thr1 = new Thread(new ThreadStart(Program.ThreadProcedure));
Thread thr2 = new Thread(new ThreadStart(Program.ThreadProcedure));

// Starting threads 
thr1.Start(); 
thr2.Start(); 

In this example, we establish two threads – thr1 and thr2 – and get them started. Thus, the ThreadProcedure function will execute twice simultaneously. Easy peasy, right? But here’s a tip: Always be cautious with concurrent writing to shared data sources, as they can potentially bring inconsistency or data corruption!

C# Debug Threads: A Closer Look

How does one debug a multi-threaded app? Good question! One way is by using the Thread window in Visual Studio. You can access it by navigating Debug -> Windows -> Threads. This window gives you insights about the thread’s ID, location, name, etc. Remember, debugging is a crucial part of development. It’s like solving a crime mystery, exhilarating when you finally find “who done it!”

Working with C# List of Threads

Listing the threads within a process can be essential when dealing with complex threading scenarios – similar to knowing all the guests at your party! Take a sneak peek:

ProcessThreadCollection currentThreads;
ProcessModuleCollection theMemModules;

int amountOfThreads = GetCurrentThreads(out currentThreads);
int amountOfModules = GetCurrentModules(out theMemModules);

Console.WriteLine("Threads for current process");
for (int i = 0; i < amountOfThreads; i++)
{
   Console.WriteLine("Thread Id: " + currentThreads[i].Id +
   ", Start Time: " + currentThreads[i].StartTime +
   ", Total Processor Time: " + currentThreads[i].TotalProcessorTime);
}

In this piece of code, we store the data of each thread in ProcessThreadCollection. Subsequently, we print out each thread’s ID, its start time, and total CPU time consumed. Just like taking attendance of everybody present!

Threading Scenarios and Techniques in C#

Ready to put your superhero cape on and dive into the practical and advanced aspects of threading? Let’s dive into examples and understand everything about max threads, and how to get the number of threads.

Practical C# Threading Example

Imagine you’re DJing a party. You’re playing music (one thread) and at the same time, accepting song requests from party-goers (another thread). This is what threading enables in C#. Here’s a simple threading example demonstrating the idea.

public class MusicDj {
  public void PlayingMusic() {
    for (int i = 0; i < 100; i++)
      Console.WriteLine("Playing the music");
  }

  public void AcceptingSongRequests() {
    for (int i = 0; i < 100; i++)
      Console.WriteLine("Accepting song requests");
  }
}

static void Main(string[] args) {
    MusicDj dj = new MusicDj();
    Thread t1 = new Thread(dj.PlayingMusic);
    Thread t2 = new Thread(dj.AcceptingSongRequests);

  // Starting the threads. Let’s get the party started!
    t1.Start();
    t2.Start();

    t1.Join();
    t2.Join();

    Console.WriteLine("End of the Party");
}

In this example, the Main() function starts two threads – t1 for “Playing the music”, and t2 for “Accepting song requests”. This is akin to a DJ handling multiple tasks at a party. When both threads finish executing (signaled by t1.Join() and t2.Join()), the party’s over. No more partying after that!

C# Max Threads: A Deeper Understanding

The maximum number of threads that can run in a C# program completely depends on the system’s resources. You might now wonder, “Okay! But how do I know the north boundary of thread creation?”

Easy! The below code is your crystal ball:

using System;
using System.Threading;

class Test
{
  static void Main()
  {
    int maxWorker, minWorker;
    int maxIOC, minIOC;

    ThreadPool.GetMinThreads(out minWorker, out minIOC);
    ThreadPool.GetMaxThreads(out maxWorker, out maxIOC);

    Console.WriteLine("Max worker threads: {0}, min worker threads: {1}", maxWorker, minWorker);
  }
}

The ThreadPool.GetMaxThreads method here helps you determine the maximum number of ThreadPool thread objects your system can handle at a given time. Remember, it’s not about how many threads you can create, it’s about how many threads your system can handle effectively!

How To Get Number of Threads in C#

Do you want to check how many threads your program has currently opened? Well, we’ve got you covered:

using System.Diagnostics;

Process process = Process.GetCurrentProcess(); // Get the current process

Console.WriteLine("Thread count: " + process.Threads.Count); // Get the number of threads

This short piece of code fetches the current process and outputs the number of threads using the Threads.Count property. It’s like counting the number of tabs open on your browser.

Advanced Threading: Timers, Tasks and Thread Communications

Heads up superheroes, the ride’s about to get more thrilling! We’ll now dive deeper into the ocean of threading, exploring timers, tasks, and intra-thread communication. Are you ready?

C# System.Timers.Timer VS System.Threading.Timer: Which One to Use?

In C#, we have two types of timers: System.Timers.Timer and System.Threading.Timer. Confused about when to use which? You’re not alone!

System.Timers.Timer is best for recurring events in a GUI environment, as it raises events in the thread pool.

On the other hand, System.Threading.Timer is ideal for precision-based tasks in console applications.

Have a look at an example that involves System.Threading.Timer:

using System;
using System.Threading;

class Example
{
    static void Main()
    {
        // Create a Timer object that executes 
        // task at 1 second intervals.
        var timer = new Timer(Task, null, 0, 1000);

        // Wait for the user to hit <Enter>
        Console.ReadLine();
    }

    private static void Task(Object o)
    {
        // Display the date/time when this method is called.
        Console.WriteLine("Task routine called at {0}", DateTime.Now);
    }
}

This tiny piece of code schedules a task method to execute after an interval of one second every time. Useful when you need a task to be performed at regular intervals, isn’t it?

C# Tasks VS Threads: Decoding the Differences

Threads and tasks appear similar, but they are worlds apart. Tasks are more abstract and are designed for parallel processing, employing multiple threads if available. Conversely, threads are more low-level and offer more control.

Task.Factory.StartNew(() =>
{
    Console.WriteLine("This is an example of Task, which can encapsulate several threads");
});

In this code, Task.Factory.StartNew() starts a task. This task can span several threads under the hood, giving you a higher level of abstraction.

C# Communication Between Threads: Best Practices

Enabling communication between threads can be quite tricky! Think of it like having a conversation with someone miles away from you, but funnily enough, while you’re also arguing with yourself. Check this:

using System;
using System.Threading;

public class Example
{
    bool done;   // Shared boolean field to test

    static void Main()
    {
        Example example = new Example();   // Create a common instance
        new Thread(example.Go).Start();
        example.Go(); 
    }

    void Go()
    {
        if (!done) { done = true; Console.WriteLine ("Done"); }
    }
}

Here, the Go() method is called from two threads. Notice that the done field is only set once because of thread synchronization. Remember, inter-thread communication is all about ensuring that threads don’t conflict with each other, and the shared data is protected.

Thread Management in C#

Now, let’s turn our attention to thread management in C#. We will discuss strategies for closing threads, and debugging techniques. Ready for this?

C# Close All Threads: When and How

“Closing thread means killing them?” Yes, you’re right! In C#, you can abort a thread using Thread’s Abort() method.

// creating thread 
Thread thr = new Thread(WriteChar);

// starting thread 
thr.Start();

// terminating thread 
thr.Abort();

With this, you’ve aborted the thread thr. Draw the curtains on it! But remember, abruptly terminating threads can lead to unpredictable behaviors. It should be your last resort!

Debugging Threads in C#

To debug threads in C#, Visual Studio provides the ‘Threads’ window. This shows information about threads currently in your process. It’s like a GPS for tracking your threads.

// Creating threads 
Thread thr1 = new Thread(Program.ThreadProcedure); 
Thread thr2 = new Thread(Program.ThreadProcedure);

// Naming threads for easy debugging
thr1.Name = "Thread1";
thr2.Name = "Thread2";

// Starting threads 
thr1.Start(); 
thr2.Start(); 

In this, we create two threads, thr1 and thr2, give them names, and start them. Now, if you open the Threads’ window (Debug -> Windows -> Threads), you can watch these threads in action with their respective names. Now that’s some innovative way to debug threads!

Conclusion

Decoding multi-threading in C# isn’t rocket science after all, right? By now, you’ve got more than just an idea about threading in C#. You’re familiar with what threads are, how to use them with examples, and even debug them!

You May Also Like

Sign up For Our Newsletter

Weekly .NET Capsules: Short reads for busy devs.

  • NLatest .NET tips and tricks
  • NQuick 5-minute reads
  • NPractical code snippets
.