9 • Asynchronous Programming in C#
9 • Asynchronous Programming in C#
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, 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.
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.
Example: 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.
Example: Creating a Task object using the Run method
In the following example, we are creating a 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 in C#:
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 the 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 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.
using System.Threading.Tasks;
namespace TaskBasedAsynchronousProgramming
class Program
return CalculateSum(10);
});
Console.ReadKey();
double sum = 0;
sum += count;
return sum;
Output:
Note: The Result property of the Task object blocks the calling thread until
the task finishes its work.
Example:
In the below example, we are writing the logic as part of the Anonymous
method.
using System;
using System.Threading.Tasks;
namespace TaskBasedAsynchronousProgramming
class Program
double sum = 0;
sum += count;
return sum;
});
Console.ReadKey();
}
It will also give you the same output as the previous example. So, whenever
your logic is a few lines and that is going to be used only once, then it is
always better to write the logic with the anonymous method.
Example: Returning Complex Type Value From a task
In the below example, we are returning a Complex type.
using System;
using System.Threading.Tasks;
namespace TaskBasedAsynchronousProgramming
class Program
ID = 101,
Name = "Pranaya",
Salary = 10000
};
return employee;
});
Console.ReadKey();
Output: