Tasks in C# Asynchronous Programming

Before reading this article kindly go through previous parts also.

Introduction 

This article explains Tasks in asynchronous programming. If we follow the previous two articles, (or if you are already familiar with Tasks), then you will find that we are returning a Task from an asynchronous function. But the question is, what is a Task? Let me give a single-line answer: "A Task is a basic unit of the Task Parallel Library (TPL)". I know that a single-line answer is not enough for understanding what a Task is, but don't worry; we will dig into it more since this article is dedicated to Tasks. (Not office tasks, tasks of asynchronous programming!!) Ok, now let's be serious and proceed to the topic.

On a basic level, a task is nothing but a unit of work. Let's try to map them with real-life tasks to understand them better.

  • A task can run/start: Real-life tasks can run/start (read proceed).
  • A task can wait: Real-life tasks wait too (my friend waited for seven days to get feedback on his first proposal, though it was negative.)
  • A task can cancel: No need to provide an example, you often cancel your task.
  • A task can have a child Task: Yes, there are subtasks in people's lives.
  • So, those are the features (better to say properties) of Tasks in asynchronous programming. A Task can only run from its start to its finish, you cannot run the same task object two times. Now, the question is, what is the solution for running the same task more than once? The answer is you will need to create another Task object to run the same task.

Ok, let's try one small example to understand Tasks.

Have a look at the following code. Here we will create an object of the Task class.

using System;    
using System.Collections.Generic;    
using System.Linq;    
using System.Text;    
using System.Threading.Tasks;     
  
namespace Asynchronious    {    
    class Program    
    {    
        public static void Main(String [] args)    
        {    
            Task t = new Task(    
                () => {    
                       System.Threading.Thread.Sleep(5000);    
                       Console.WriteLine("Huge Task Finish");     
                     }    
                );     
  
            //Start the Task    
            t.Start();    
            //Wait for finish the Task    
            t.Wait();    
            Console.ReadLine();    
        }    
    }    
}

We are calling the Start() method to start the Task. After that, we are calling the Wait() method that implies we are waiting for the task to finish. Here is the sample output.

Asynchronous1.jpg

How to Wait for a Task?

Let's try to understand how to delay (or sleep) a Task for a while. Have a look at the following example.

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
  
namespace Asynchronious  
{  
    class Program  
    {  
        public static void Main(String [] args)  
        {  
            Task t = new Task(  
                () => {  
                       System.Threading.Thread.Sleep(5000);  
                       Console.WriteLine("Huge Task Finish");   
                     }  
                );   
  
            //Start the Task  
            t.Start();  
  
            //Wait for 1 second  
            bool rValue = t.Wait(1000);  
            Console.WriteLine("Main Process Finished");  
            Console.ReadLine();  
        }  
    }  
}

In this example, we are waiting to finish a huge task for one second even after it has actually finished. So, we learn how to continue to wait for a Task. Here is the output screen.

Asynchronous2.jpg

Implement Child Task

Here we will implement a child Task of another Task. Have a look at the following code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Asynchronious {
  class Program {
    public static void Main(String[] args) {
      Task Parent = new Task(
        () => {
          Task Child = new Task(
            () => {
              System.Threading.Thread.Sleep(2000);
              Console.WriteLine("Inner Task Finish");
            },
            TaskCreationOptions.AttachedToParent
          );

          //Start Child Task  
          Child.Start();
          System.Threading.Thread.Sleep(2000);
          Console.WriteLine("Outer Task Finish");
        }
      );

      //Start the Task  
      Parent.Start();
      Parent.Wait();
      Console.ReadLine();
    }
  }
}

At first we created a Parent Task that contains another child Task within it. The innermost and outermost Tasks will sleep for two seconds. We are running a child Task within the object of a parent Task.

Asynchronous3.jpg

Now, let's analyze the output. We see that at first, the outer Task is finishing, and then the innermost Task finishes. Why is that? It is happening due to an asynchronous call. The outer Task is running the innermost Task, but not waiting for it. This is the beauty of an asynchronous call.

Get Status of Task

We can detect the status of any Task. Let's see in the following example.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Asynchronious {
  class Program {
    public static void Main(String[] args) {
      Task t = new Task(
        () => {
          System.Threading.Thread.Sleep(5000);
        });

      t.Start();
      t.Wait();
      Console.WriteLine(t.Status);
      Console.WriteLine("End of Main");
      Console.ReadLine();
    }
  }
}

Here, we are starting the Task and then we are waiting for the Task to be complete. In the next line, we are checking the status of the Task using the Status property of the (Task) object. Now a question may arise: What is the purpose of checking the status? There are many things to do, so we can run another Task depending on the status of another Task. Let's see the output:

Asynchronous4.jpg

The status is showing RunToCompletion. It means that the Task is running currently and it will complete.

Few more properties of Task class

Let's check a few more properties of the Task class. Have a look at the following example.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Asynchronious {
  class Program {
    public static void Main(String[] args) {

      Task t = new Task(
        () => {
          System.Threading.Thread.Sleep(5000);

        });

      t.Start();
      Console.WriteLine("Cancelled:- " + t.IsCanceled);
      Console.WriteLine("Completed:- " + t.IsCompleted);
      Console.WriteLine("Folted:- " + t.IsFaulted);
      Console.WriteLine("End of Main");
      Console.ReadLine();
    }
  }
}

Here we will check a few statuses of the Task object. For example, we are interested in checking for the Cancel, Completed and Failed statuses of the Task. In the example all are False. That means.

  • Cancelled: The Task is not Cancelled
  • Completed: It is not completed (still running)
  • Faulted: There is no error or exception to run this Task.

Asynchronous5.jpg

Conclusion

In this article, we have learned what a Task is and the various properties of tasks. In future articles, we will concentrate on exception handling in asynchronous programming. Keep on reading this series. Hey!! Are you still reading? Then that means both of us love asynchronous programming! Have a nice day.


Similar Articles