In this article I explain asynchronous methods calls. Let us first discuss what asynchronous and synchronous calls are. Suppose we have a function Print() that is taking one second to execute and this function is called by the main
function (calling function).
public void print()
{
Thread.Sleep(1000);
Console.WriteLine("Hello World");
}
So after one second it will print "Hello World" and the control will return to the calling function. This is called a synchronous call. Suppose we want to call this function 100 times. So the total time it will take is 100*1000 seconds which is bad because unless the time does not elapse the control will not be able to execute other lines of code.
In an asynchronous call, rather than waiting for the code to complete, the call returns immediately to the calling function; or you can say "Call and Forget".
The essence of asynchronous calls come from delegates Luckily in C# it is very easy to implement delegates; you just need to declare the delegate, instantiate it and call it. Let us see that in the following example::
class Program
{
public delegate void mymethod();
static void Main(string[] args)
{
mymethod inv = new mymethod(print);
inv.BeginInvoke(null, null);
Console.WriteLine("I am Back");
Console.Read();
}
public static void print()
{
Thread.Sleep(1000);
Console.WriteLine("Hello World");
}
}
As you can see, I have declared a delegate ("mymethod" in the example) and created an object of it in the main function. The delegate will call the print method. Here I have used BeginInvoke() rather than using Invoke() because if I would have used Invoke it would again be a synchronous call. Don't worry about the parameters, I will cover that later.
When we call a method to run asynchronously it does not do in the same thread because that would make it a synchronous call; instead it requests a new thread of the .Net thread pool to execute the function. You need to notice that the thread pool has its own limit of only 25 threads so if all the threads are busy then I will wait for at least one thread to get free; as soon as it gets free the control will acquire it and execute the method in it.
Until now we have no idea of whether the function executing through the delegate has completed its execution, to understand it let us suppose an another scenario where we want to print "I am Not completed yet" just after the control returns and when asynchronous method execution is over we want to print "Execution over ". To know whether our method Print() has executed or not we will use EndInvoke(). BeginInvoke() returns an IAsynchResult object and this is the thing that really binds EndInvoke() and BeginInvoke().
class Program
{
public delegate void mymethod();
static void Main(string[] args)
{
mymethod obj = new mymethod (print);
IAsyncResult tag= obj.BeginInvoke(null, null);
Console.WriteLine("I am not completed yet");
obj.EndInvoke(tag);
Console.WriteLine("Execution Over");
}
public static void print()
{
Thread.Sleep(1000);
Console.WriteLine("Hello World");
}
}
Let's see how to pass a parameter to our method through a delegate and begin invoke. Well it is quite simple to declare a parameter at the same time as the declaration of the delegate. During invocation of the delegate we will pass the value. In my example I am passing "Hello World".
public delegate void mymethod(string a);
static void Main(string[] args)
{
mymethod obj = new mymethod (print);
IAsyncResult tag= obj.BeginInvoke("Hello World",null, null);
Console.WriteLine("I am not completed yet");
obj.EndInvoke(tag);
Console.WriteLine("Execution Over");
}
public static void print(string param)
{
Thread.Sleep(1000);
Console.WriteLine(param);
}
Let's make our example a bit more complex. What if we want our print method to return a value; suppose a datatable. Here comes an AsyncCallback delegate. Create an instance of the AsyncCallback delegate and pass it to the BeginInvoke method. When you pass an AsyncCallback to BeginInvoke, the delegate will invoke that callback upon completion of the asynchronous operation. Then, you can use that opportunity to call EndInvoke to retrieve the return value.
class Program
{
public delegate DataTable mymethod(string s);
public static DataTable dt;
public static mymethod inv;
static void Main(string[] args)
{
inv = new mymethod(Print);
inv.BeginInvoke("ahmar", new AsyncCallback(Callback), null);
Console.ReadLine();
}
public static DataTable Print(string q)
{
Thread.Sleep(1000);
Console.WriteLine(q);
DataTable dt = new DataTable();
dt.Columns.Add("Age");
dt.Rows.Add(11);
dt.Rows.Add(12);
dt.Rows.Add(13);
return dt;
}
public static void Callback(IAsyncResult t)
{
dt = inv.EndInvoke(t);
foreach (DataRow row in dt.Rows)
{
Console.WriteLine(row["age"].ToString());
}
}
}
Output
Ahmar
11
12
13