Dot Net For All

C# Task Continuation, Exceptions and Result

Tasks Introductions

In one of my article I have briefed about the task and in this article I have discussed about the differences between the threads and task. In this article I will discuss about the task in C# with real world code example for handling continuations, exception handling and returning results from the tasks which will make you understand why and how we can use task in C#.

Task in C# Example

For this project I have created an ASP.NET MVC web application whose controller I will be calling from my client to create a scenario for time consuming operation. The project structure is as shown in the below figure. ForThreading is an MVC web app which I will be calling from the ThreadingApplication which is a console application.

I have set both the projects as multiple startup projects.

 

In the Home controller of the web application for the index method I have made the current thread to wait for 5 seconds as shown in the below code.

        public ActionResult Index()
        {
            Thread.Sleep(5000);
            return View();
        }

Code Snippet 1

Calling the Code Synchronously

The below code I will call synchronously without using tasks or any other way for asynchronous programming.

static Stopwatch stopWatch = new Stopwatch();
        static void Main(string[] args)
        {
            stopWatch.Start();
            Console.WriteLine("Issue the request");
            DownloadString();
            Console.WriteLine("Completed in :" + stopWatch.Elapsed);
            Console.Read();
        }

        public static void DownloadString()
        {
            using (WebClient webCleint = new WebClient())
            { 
                string siteContent = webCleint.DownloadString("http://localhost:57196/Home");
                Console.WriteLine(siteContent);
            }
        }

Code Snippet 2

Please note that I am calling the web project and downloading the string content of the Home view hosted at http://localhost:57196 at my local machine. The port can be something else for you for which you have to do the required changes.

If I run the project I will get the output as shown in the below figure

 

As we can see from the above figure time taken to complete the whole operation is 14 seconds which means that result from the synchronous method is not returned unless and until the whole time consuming operation is not complete. If this is an UI based application and if we are running on  the main thread in that case our UI will be blocked for the time when operation is performed which can lead to bad user experience.

Calling the Long Running Code Using Task

The same operation can be performed without blocking the main thread using tasks. The below code will run asynchronously by creating a thread in thread pool.

        public static void DownloadStringAsyncUsingTask()
        {
            Task<string> task = Task<string>.Factory.StartNew(() =>
            {

                WebClient webCleint = new WebClient();
                string siteContent = webCleint.DownloadString("http://localhost:57196/Home");
                return siteContent;
            });

            task.ContinueWith(t => {
                Console.WriteLine(t.Result);               
            });
        }

Code Snippet 3

Run the above code by replacing the DownloadString() method by DownloadStringAsyncUsingTask() in the Main method. The output of the above code will be as shown figure.

As shown in the above figure the control is returned to the main function soon after executing the DownloadStringAsyncUsingTask() method which signifies that this is a completely responsive operation and execution of the time consuming function is taken care by Task.

I have created a task in the method using Task.Factory.StartNew method provided by the Task class which returns an await-able task in C#.

Continuations in Tasks

Continuations are mechanism by which we can continue with the result of the main task after it has done with performing the time consuming operation, by calling the ContinueWIth() method on the primary task.  The task in the code snippet 3 expects a result of type string for the asynchronous operation which I am returning in the action delegate which I have created in the StartNew() method.

The continuation method accepts an action method which accepts a parameter of type Task as shown in the above figure. As shown in the code snippet 3 I am continuing for the main task. And in the continue method I am writing the result on the console.

Exception Handling in Tasks

There can be chances that when the Task can result in the error state due to some exception in the function block as shown in the figure below.

As shown in the above figure I have called a Test controller in place of my Home controller which results in the exception which is not handled till now in the code and if we continue with the same code as Code snippet 3 we will not get a result for the operation keeping the user of the code wondering what happened with the task.

There are two ways we ways we can check for the exception in the tasks.

  1. Status – We can check for the status property of the task and if is faulted which means that task has not completed in usual way as shown in the below code.
               task.ContinueWith(t => {                
                    if (t.Status == TaskStatus.Faulted)
                    {
                        Console.WriteLine(t.Exception);
                    }
                    else
                    {
                        Console.WriteLine(t.Result);
                    }
                });
  2. IsFaulted – By checking the IsFaulted property of the task as shown below-
                task.ContinueWith(t => {                
                    if (t.IsFaulted)
                    {
                        Console.WriteLine(t.Exception);
                    }
                    else
                    {
                        Console.WriteLine(t.Result);
                    }
                });

Conclusion

In this article I have demoed how using the Task in C# can be helpful for long running operation and how we can attach continuations and exception handling to handle the faulty tasks.

In my next article I will discuss how the same operation can be handled without creating threads.

 

Top career enhancing courses you can't miss

My Learning Resource

Excel your system design interview