Task-based Asynchronous Programming in C#

Task-based Asynchronous Programming in C#

In this article, I am going to discuss Task-based Asynchronous Programming in C# with some examples. Please read our multithreading articles before proceeding to this article. In C#.NET, the task is basically used to implement Asynchronous Programming i.e. executing operations asynchronously and it was introduced with .NET Framework 4.0.

Before understanding theory i.e. what is Task and what are the benefits of using Task, let us first discuss how to create and use Task in C#.

Working with Task in C#:

The Task-related classes belong to System.Threading.Tasks namespace. So first and foremost step for you is to import the System.Threading.Tasks namespace in your program. Once you import the System.Threading.Tasks namespace, then you can create as well as access the task objects by using Task class.

Note: In general, the Task class will always represent a single operation and that operation will be executed asynchronously on a thread pool thread rather than synchronously on the main thread of the application. If this is not clear at the moment then don’t worry we will discuss this in practice.

Example1: Using the Task class and Start method

In the below example, we are creating the task object by using the Task class and then start executing it by calling the Start method on the Task object.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskBasedAsynchronousProgramming
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"Main Thread : {Thread.CurrentThread.ManagedThreadId} Statred");
            Task task1 = new Task(PrintCounter);
            task1.Start();
            Console.WriteLine($"Main Thread : {Thread.CurrentThread.ManagedThreadId} Completed");
            Console.ReadKey();
        }

        static void PrintCounter()
        {
            Console.WriteLine($"Child Thread : {Thread.CurrentThread.ManagedThreadId} Started");
            for (int count = 1; count <= 5; count++)
            {
                Console.WriteLine($"count value: {count}");
            }
            Console.WriteLine($"Child Thread : {Thread.CurrentThread.ManagedThreadId} Completed");
        }
    }
}

In the above example, we created the task object i.e. task1 using the Task class and then call the Start method to start the task execution. Here, the task object task1 will create a new child thread to execute the defined functionality asynchronously on a thread pool thread. So, when you run the above application, you will get the following output.

Task-based Asynchronous Programming in C#

As you can see in the above output, two threads are used to execute the application code. The main thread and the child thread. And you can observe both threads are running asynchronously.

Example2: Creating a task object using Factory Property

In the following example, we are creating the task object using the Factory property which will start automatically.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskBasedAsynchronousProgramming
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"Main Thread : {Thread.CurrentThread.ManagedThreadId} Statred");
            Task task1 =  Task.Factory.StartNew(PrintCounter); 
            Console.WriteLine($"Main Thread : {Thread.CurrentThread.ManagedThreadId} Completed");
            Console.ReadKey();
        }

        static void PrintCounter()
        {
            Console.WriteLine($"Child Thread : {Thread.CurrentThread.ManagedThreadId} Started");
            for (int count = 1; count <= 5; count++)
            {
                Console.WriteLine($"count value: {count}");
            }
            Console.WriteLine($"Child Thread : {Thread.CurrentThread.ManagedThreadId} Completed");
        }
    }
}

It will give you the same output as the previous example. The only difference between the previous example and this example is here we creating and running the thread using a single statement.

Example3: Creating a Task object using the Run method

In the following example, we are creating a started task by using the Run method of the Task class.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskBasedAsynchronousProgramming
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"Main Thread : {Thread.CurrentThread.ManagedThreadId} Statred");
            Task task1 = Task.Run(() => { PrintCounter(); });
            Console.WriteLine($"Main Thread : {Thread.CurrentThread.ManagedThreadId} Completed");
            Console.ReadKey();
        }

        static void PrintCounter()
        {
            Console.WriteLine($"Child Thread : {Thread.CurrentThread.ManagedThreadId} Started");
            for (int count = 1; count <= 5; count++)
            {
                Console.WriteLine($"count value: {count}");
            }
            Console.WriteLine($"Child Thread : {Thread.CurrentThread.ManagedThreadId} Completed");
        }
    }
}

So, we have discussed three different ways to create and start a task in C#. From a performance point of view, the Task.Run or Task.Factory.StartNew methods are preferable to create and schedule the tasks.

But, if you want to the task creation and scheduling separately, then you need to create the task separately by using Task class and then call the Start method to schedule the task execution for a later time.

Task using Wait:

As we already discussed, the tasks will run asynchronously on the thread pool thread and the thread will start the task execution asynchronously along with the main thread of the application.

So far the examples we discussed in this article, the child thread will continue its execution until it finishes its task even after the completion of the main thread execution of the application.

If you want to make the main thread execution wait until all child tasks are completed, then you need to use the Wait method of the Task class. The Wait method of Task class will block the execution of other threads until the assigned task has completed its execution.

In the following example, we are calling the Wait() method on the task1 object to make the program execution wait until the task1 completes its execution.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskBasedAsynchronousProgramming
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"Main Thread : {Thread.CurrentThread.ManagedThreadId} Statred");
            Task task1 = Task.Run(() => 
            {
                PrintCounter();
            });
            task1.Wait();
            Console.WriteLine($"Main Thread : {Thread.CurrentThread.ManagedThreadId} Completed");
            Console.ReadKey();
        }

        static void PrintCounter()
        {
            Console.WriteLine($"Child Thread : {Thread.CurrentThread.ManagedThreadId} Started");
            for (int count = 1; count <= 5; count++)
            {
                Console.WriteLine($"count value: {count}");
            }
            Console.WriteLine($"Child Thread : {Thread.CurrentThread.ManagedThreadId} Completed");
        }
    }
}

As you can see in the above code, we are calling the Wait() method on the task object i.e. task1. So, the main thread execution will wait until the task1 object completes its execution. Now run the application and see the output as shown in the below image.

Wait method in Task-based Asynchronous Programming

So as of now, we have discussed how to work with threads using different approaches. Now let us discuss what is Task and why should we use Task?

What Is A Task In C#?

A task in C# is used to implement Task-based Asynchronous Programming in C# and was introduced with the .NET Framework 4. The Task object is typically executed asynchronously on a thread pool thread rather than synchronously on the main thread of the application.

A task scheduler is responsible for starting the Task and also responsible for managing it. By default, the Task scheduler uses threads from the thread pool to execute the Task.

What is Thread pool in C#?

A Thread pool in C# is a collection of threads which can be used to perform a number of task in the background. Once a thread completes its task, then again it sent to the thread, so that it can be reused. This reusability of threads avoids an application to create a number of threads which ultimately uses less memory consumption.

Why do we need to use a Task In C#?

Tasks in C# basically used to make your application more responsive. If the thread that manages the user interface offloads the works to other threads from the thread pool, then it can keep processing user events which will ensure that the application can still be used.

That’s it for today. In the next article, I am going to discuss how to return a value from a task in C# with some examples. Here, in this article, I try to explain Task-based Asynchronous Programming in C# using the Task class. I hope you understood how to create and use Task class objects in C#.

Leave a Reply

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