This is the continuation of my previous article on Tasks, where we discussed the basic understanding of Tasks and their usage. Continuing with that, here we will discuss some more functionality and behavior of Task.
How to wait for Tasks to complete?
As discussed in my earlier blog, Task run asynchronously on a thread from the Thread Pool. The thread starts task execution asynchronously along with the main application thread as soon as it has been invoked.
We also see an example in our previous discussion on Task, where both threads (Main and Child) started their execution simultaneously (asynchronously), the Main thread didn’t wait for the Child thread to complete. The Child thread continued its execution even after the completion of Main thread.
In cases, where you want your main application thread execution to wait until other child Tasks complete its execution, then you can do that in Task by using the Wait() method.
Let’s check this concept through an example.
Example: 1 – Create a Task and wait for the Task to complete
In below example, we have created a method PrintOddEvenNumbers() that prints odd and even numbers. In the Main method, we have created a task instance Task t = new Task(PrintOddEvenNumbers); and then invoked it by calling the t.start()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using System; using System.Threading.Tasks; namespace TaskInCshap { class Program { static void Main(string[] args) { //Create a task instance Task t = new Task(PrintOddEvenNumbers); t.Start(); //Waiting for child thread to complete t.Wait(); Console.WriteLine("In Main Method, Finished work on thread : {0}", System.Threading.Thread.CurrentThread.ManagedThreadId); Console.Read(); } public static void PrintOddEvenNumbers() { Console.WriteLine("Even and Odd numbers less than 10"); //Print even numbers for (int i = 0; i <= 10; i++) if (i % 2 == 0) Console.WriteLine("Even Number : {0}", i); //Print odd numbers for (int i = 0; i <= 10; i++) if (i % 2 != 0) Console.WriteLine("Odd Number : {0}", i); Console.WriteLine("In Child Method, Finished work on thread : {0}", System.Threading.Thread.CurrentThread.ManagedThreadId); } } } |
In the above example, we have used t.Wait() method for Task t object. Which will make the Main thread to wait until the Child thread finishes its execution.
When we run the above program, we will get below output.

If you notice in the above output console, here the Main thread waits for the Child thread to finish its execution.
Get return value from Tasks?
There are scenarios when you need to return a value from a task, in that case you can use the Task<T> class, where T is the type of the value you want to return. This allows you to run asynchronous operations and retrieve results after the task completes.
Example: 2 – Return a value from Task using Task<T>
In below example, we have created a method as SumOfAllNumber that takes an integer array as input and calculates the sum of values present in the array, finally returns the calculated sum value.
- In the Main method, we have created a task instance Task<int> task = new Task<int>(() => SumOfAllNumber(arrNum)) that has the return type as Task<int>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
using System; using System.Threading.Tasks; namespace TaskInCshap { class Program { static void Main(string[] args) { int[] arrNum = new int[] { 13, 14, 25, 30, 56, 11, 20, 29 }; //Create a Task<TResult> instance, return type is int Task<int> task = new Task<int>(() => SumOfAllNumber(arrNum)); task.Start(); //Get the return value from Task int sum = task.Result; Console.WriteLine("Sum of all numbers in array : {0}", sum); Console.WriteLine("In Main Method, Finished work on thread Id: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId); } private static Int32 SumOfAllNumber(int[] arrNum) { int sum = 0; for (int i = 0; i < arrNum.Length; i++) sum = sum + arrNum[i]; Console.WriteLine("In Child Method, Finished work on thread Id : {0}", System.Threading.Thread.CurrentThread.ManagedThreadId); return sum; } } } |
When we run above example then will get below output.

If you look at the output, here both Main method and Task executed on different thread. However, since we are using Task.Result property in the Main method to read the return value from the Task. So, that’s why the child thread blocked the Main method until it finished execution.
Example: 3 – Return a value from using Task.FromResult()
There could be scenarios where you want to return a value from a task immediately even without performing the asynchronous work, in that case you can use Task.FromResult
Let’s take below example.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using System; using System.Threading.Tasks; namespace TaskInCshap { class Program { static async Task Main(string[] args) { string result = await GetPositiveOrNegativeValueAsync(5); Console.WriteLine($"Message: {result}"); } static Task<string> GetPositiveOrNegativeValueAsync(int number) { if (number > 0) { return Task.FromResult("Positive number"); } else { return Task.FromResult("Non-positive number"); } } } } |
In this above example, Task.FromResult is used to return data based on a conditional check. If the condition is met, the data is returned immediately.

Example: 4 – Returning a Complex Types from Task using Task<T>
You can also return complex types, such as custom objects or collections, from a task by passing the object type in the type T parameter.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
using System; using System.Threading.Tasks; namespace TaskInCshap { class Program { static async Task Main(string[] args) { Student student = await GetStudentDetailAsync(); Console.WriteLine("Retriving Complex object detail by calling the method : GetStudentDetailAsync"); Console.WriteLine($"Name: {student.Name}, Age: {student.Age} , School : {student.School} , Section : {student.Section}"); } static async Task<Student> GetStudentDetailAsync() { await Task.Delay(500); // this has added to put a delay return new Student { Name = "David", Age = 25,School ="Oxford", Section = "A" }; } } class Student { public string Name { get; set; } public int Age { get; set; } public string School { get; set; } public string Section { get; set; } } } |
When we run above program, we will get output as below.

In this case, GetStudentDetailAsync() method returns a Student object after having a delay to simulate some work. You can use this approach to return any complex type from a task.
Conclusion:
In C#, you can easily get a return value from a task using the Task<T> type. The await keyword is the common way to retrieve the result of an asynchronous operation without blocking the main thread. Thanks for your time 🙂 Please provide your feedback and keep visit for more blogs.
