Lesson 1 - Introduction to multi-threaded applications in C#.NET

C# .NET Parallel programming Introduction to multi-threaded applications in C#.NET

Welcome to the first lesson about programming multi-threaded applications in C# .NET. We're going to learn how to use modern multi-core processors efficiently, and also how to run tasks in the background, preventing the main application thread from freezing. We'll get to the latest technologies from the .NET framework such as parallel programming, Tasks or the async and await keywords.

Introduction to the thread theory

Before we start using threads, let's describe how our operating system runs applications. Since we're learning C#, we can talk about Windows, but the principle can be applied for most other operating systems. Windows is a multi-tasking operating system. This means that it can run multiple applications at once. Nevertheless a processor core can usually run only one instruction at one time. You know that Windows was able to run multiple applications at a time long before multi-core processors were even on the market, and now you can also run as many applications as you want, no matter how many cores your PC's processor has. How is it possible?

Windows is simply switching between the running applications very quickly (more precisely, this is done by the scheduler, and we refer to the switching as to time-slicing). The system stops one application and starts another one for a moment. The user sees it as all the applications were running at once, even though they do not. With multi-core processors, this principle hasn't changed, Windows still switches between applications but can execute multiple instructions at the same time on each processor core.

Application, Process, and Thread

Let's think about the terms application, process, and thread. We can certainly imagine an application, it is, for example, this web browser in which you're reading this article. However, what's a process and what's a thread?

Process

A process is an instance of a running application. If we run a calculator three times, we can find the calc.exe process three times in the task manager. Some more complex applications can use several processes, e.g. Google Chrome creates a process for each open tab. However, in most cases, each application has only one process.

Thread

There can be multiple threads running in one process. Each application has at least one process in which the main thread or, eventually, some other threads run. While the main thread is created for us automatically, we create other threads on our own. The operating system runs our thread on a core, then quickly puts it to sleep and wakes it up again as it needs. As a result, the threads in the process run in parallel and we can, for example, perform complex analyses without blocking the main application thread and even using several threads at a time, while the calculation itself will be several times faster.

Synchronization

Everything sounds great, doesn't it? However, in practice, threads are usually used only if we really need them. There's a huge problem with them - synchronization. We never know when our threads will be put to sleep. The system will do it no matter what the thread is doing. We can imagine that the thread method gets stuck on some line of the source code. In fact, however, it can also get stuck, for example, in the middle of the addition operation, because two instructions are needed to be executed to add 64-bit numbers on a 32-bit processor. If another thread is using this value, it may result in nonsense. Variables are also cached in the cores, so the same variable can have multiple different values at the same time. In this course, we'll get to the synchronization problems in detail and also learn how to solve them.

Threads are certainly not something that every application must necessarily have, no matter Microsoft engineers are trying their best to as easy to use as possible (and they are quite successful in it). The application still becomes more complicated with this technology. Be sure to use them with caution.

First multi-threaded application

Let's program our first multi-threaded application. For the sake of simplicity, we'll only work in the console for a while. Create a new project and name it Switcher. We'll add a class of the same name to the project with the following contents:

class Switcher
{

    public void Write0()
    {
        while (true)
        {
            Console.Write("0");
        }
    }

    public void Write1()
    {
        while (true)
        {
            Console.Write("1");
        }
    }

    public void Switch()
    {
        Thread thread = new Thread(Write0);
        thread.Start();
        Write1();
    }

}

The first two methods of the class are very simple and simulate a long activity. The first method prints zeroes to the console continuously, the second method prints ones in the same way.

The Switch() method is more interesting for us. It calls the method printing ones on its own. But before that, it creates a new thread and assigns the method printing zeros to it. It also starts this new thread.

In .NET, threads are represented by the Thread class. We pass the ThreadStart delegate to it through the constructor. It has the following declaration:

public delegate void ThreadStart();

So, we can pass a parameter-less method of the void type to a thread. In the main() method, we'll create a Switcher instance and let it switch:

static void Main(string[] args)
{
    Switcher switcher = new Switcher();
    switcher.Switch();
}

When we run the app, we get this output:

Switching threads in C# .NET

Notice that the ones and zeroes are not switching equally as we might expect. We can see how each thread runs for a while and then is put to sleep. The intervals also differ, even though on average, both threads are running for the same amount of time.

Tread class properties

So far, we're interested in the following properties of the Thread class:

  • IsAlive - Indicates whether the thread method is running.
  • Name - We can give a name to every thread, which makes debugging the application easier.

The main application thread

We get the main application thread using the static Thread.CurrentThread property. Unfortunately, no name is assigned to it by default. Let's try to assign a name to it and print it:

static void Main(string[] args)
{
    Thread.CurrentThread.Name = "Main thread";
    Console.WriteLine(Thread.CurrentThread.Name);
}

In the next lesson, Threads in C# .NET - Sleep, Join, and lock, we'll look at putting threads to sleep, joining them and we'll go through the basics of synchronization.


 

 

Article has been written for you by David Capka
Avatar
Do you like this article?
No one has rated this quite yet, be the first one!
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn College The author learned IT at the Unicorn College - a prestigious college providing education on IT and economics.
Activities (2)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!