C# Threads Tutorial and Examples

C# Threads Tutorial and Examples

Towards the end of the 20th century, computers could already work with several programs simultaneously. This is normally called multitasking.

However, only one computing unit had to be executed at a given time. So the risk was that one program could monoplize the CPU, causing other applications and the Operating System itself to wait for ever.

So the Operating System designers decided to split a physical unit across a few virtualized processors in some way. This could then give a certain amount of processor time to each executing program.

Besides, the Operating System had to be given higher priority to the processor time. Then the Operating System could prioritize the CPU access across different programs.

This implementation is what we call a thread.

A thread is a path of execution that exists within a process. Threads are like virtual processors assigned to a specific programs. The program then runs the thread independently.

A process is an instance of the program that is being executed.A process can comprise one or more threads.

Processors were becoming faster and faster, being able to execute more instructions per second.

But later on, modern processors started to have more computing cores, instead of becoming faster. However, programs written in the older way could not take advantage of this increase in power via multiple cores since they were designed to run on a single core processor.

So nowadays it is important to write programs that can use more than one computing core. This allows them to effectively utilize the modern processors power.

To achieve this we can execute tasks using multiple threads. The threads should then be able to properly communicate and synchronize with each other.

Creating a Thread

The Thread class is defined in the System.Threading namespace.

using System.Threading;

Let's look at an example of creating a thread that prints out stars defined in an array. In fact we have two arrays: one stars array and the other nebulas array.

The stars array will be creating and rendered from our background thread while the nebulas array will be created and printed out from main thread.

using System;
using System.Threading;

namespace MsThread
{
    class Program
    {
        static void Main()
        {
            Thread t = new Thread(showStars);
            t.Start();
            showNebulas();
            Console.ReadLine();

        }
        static void showNebulas()
        {
            Console.WriteLine("Main Thread...................");
            string[] nebulas = {"Horse Head","Ghost Head","Orion","Pelican","Witch Head","Helix","Boomerang","Bernad 68"};
            foreach (var nebula in nebulas)
            {
                Console.WriteLine(nebula);
            }
            Console.WriteLine();

        }
        static void showStars()
        {
            Console.WriteLine("Starting Stars Thread.............");
            string[] nebulas = { "UY Scuti", "VY Canis Majoris", "VV Cephei A","NML Cygni", "Betelgeuse","Antares","Rigel","Aldebaran" };
            foreach (var nebula in nebulas)
            {
                Console.WriteLine(nebula);
            }
        }
    }
}

Result

Main Thread...................
Horse Head
Ghost Head
Orion
Pelican
Witch Head
Helix
Boomerang
Bernad 68

Starting Stars Thread.............
UY Scuti
VY Canis Majoris
VV Cephei A
NML Cygni
Betelgeuse
Antares
Rigel
Aldebaran

We create a thread by instantiating the System.Threading.Thread class.

Then we pass an instance of ThreadStart or ParameterizedThreadStart delegate via the constructor.

The C# compiler will then create an object behind the scenes as long as we pass the name of the method we want to run in the different thread.

To start the thread we use the start() method.

The showNebulas() on the other hand will be run in the main thread.

Race Condition

Implementing Threads in applications is not an ardous task.

Tasks can divided implemented in independent threads and the threads can be scaled along with the number of CPU cores.

The problem comes when those threads need to interact with each other. And this is something which is more common in real world projects than you would imagine.

When they require exchanging some information. This requires allocation of space in RAM that is accessible to both threads.

And this is the root of many of the much talked about problems in parallel programming.

The first and one of the biggest of these is called race condition. This comes due to undefined access order. We can get incorrect results from calculations due because multiple threads can access and read or write the shared variables in an unpredictable order.

ThreadStart and ParemeterizedThreadStart

ThreadStart

A ThreadStart is a delegate defined in the System.Threading namespace that represents the method that executes on a Thread.

Threads are lines of executions within a process and allow us write concurrent programs.

To be able to do this we need to pass them the method that needs to be run in the background.

Normally we use the ThreadStart delegate to hold the reference to that method.

Typically, methods don't get passed around constructors or other methods. But delegates allow us to do this, so the ThreadStart delegate allows us pass methods to the Thread constructor.

The ThreadStart class is defined in the following manner:

public delegate void ThreadStart();

This is a top level object inside the System.Threading namespace, which is itself defined in the mscorlib.dll assembly.

Let's look at an example:

using System;
using System.Threading;

namespace MrThreadStart
{
    class Program
    {
        static void Main()
        {
            ThreadStart myThreadStart=showGalaxies;
            Thread myThread = new Thread(myThreadStart);
            myThread.Start();
            Console.ReadLine();

        }
        static void showGalaxies()
        {
            Console.WriteLine("Starting Galaxies Thread.............");
            string[] nebulas = { "IC 1101", "Andromeda", "Centaurus A","Milky Way", "Cartwheel","Sombrero","Black Eye Galaxy","Pinwheel" };
            foreach (var nebula in nebulas)
            {
                Console.WriteLine(nebula);
            }
        }
    }
}

Result

Starting Galaxies Thread.............
IC 1101
Andromeda
Centaurus A
Milky Way
Cartwheel
Sombrero
Black Eye Galaxy
Pinwheel

As you can see we first instantiate the ThreadStart delegate and assign it the method to hold.

You can do this in either of the following two ways:

  1. ThreadStart myThreadStart=new ThreadStart(showGalaxies);
  2. ThreadStart myThreadStart=showGalaxies;

The first version is the explicit way to create our delegate.

The important thing is to assign it the method that it will hold. Then instantiate the Thread class and pass the ThreadStart delegate instance to the constructor:

Thread myThread = new Thread(myThreadStart);

Then start the thread:

myThread.Start();

ParameterizedThreadStart

ParameterizedThreadStart is a delegate defined in the System.Threading namespace that represents the method that executes on a thread.

Unlike the ThreadStart delegate, it takes in a parameter of type Object. That Object contains the data for the thread procedure.

public delegate void ParameterizedThreadStart(object obj);

Let's look at an example.

using System;
using System.Threading;

namespace MrParameterizedThreadStart
{
    class Program
    {
        static readonly string[] planets = { "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" };
        static void Main()
        {
           // ParameterizedThreadStart myParameterizedThreadStart = new ParameterizedThreadStart(o => showGalaxies(planets));
            ParameterizedThreadStart myParameterizedThreadStart = o => showGalaxies(planets);
            Thread myThread = new Thread(myParameterizedThreadStart);
            myThread.Start();
            Console.ReadLine();

        }
        static void showGalaxies(string[] myPlanets)
        {
            Console.WriteLine("Starting Planets Thread.............");
            foreach (var planet in myPlanets)
            {
                Console.WriteLine(planet);
            }
        }
    }
}

Results

Starting Planets Thread.............
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune

We are assigning a lambda expression to our ParameterizedThreadStart instance:

ParameterizedThreadStart myParameterizedThreadStart = o => showGalaxies(planets);

We can also explicitly instantiate the ParameterizedThreadStart:

ParameterizedThreadStart myParameterizedThreadStart = new ParameterizedThreadStart(o => showGalaxies(planets));

We need to make sure that the method whose address we are holding in our delegate takes in a parameter for us to use the ParameterizedThreadStart:

static void showGalaxies(string[] myPlanets){}

Then we will instantiate the Thread and pass it our ParameterizedThreadStart instance:

Thread myThread = new Thread(myParameterizedThreadStart);

Then start the thread:

 myThread.Start();

Best Regards.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *