Example tutorial on asynchronous Invocation of C foundation

  • 2020-10-07 18:51:58
  • OfStack

This article demonstrates the implementation method of asynchronous call in C# in the form of an example, and makes a more in-depth analysis of its principle, and is now Shared with you in the form of a tutorial for your reference. The details are as follows:

Let's start with a simple example:

Xiao Ming was boiling water, and when the water came to a boil, he filled the thermos with boiling water and began to tidy up the house
Xiaowen in boiling water, in the process of boiling water tidy up the housework, such as water boil, put down the hands of housework, the water into the thermos, and then continue to tidy up the housework
This is also a very common situation in daily life, Xiaowen's efficiency is obviously higher than Xiaoming's. From the perspective of C# program execution, Xiaoming USES synchronous processing mode, while Xiaowen USES asynchronous processing mode.

In the synchronous processing mode, transactions are processed sequentially piece by piece. In the asynchronous case, the child operation is separated from the main operation, the main operation continues, and the child notifies the main operation when it has finished processing.

In C#, asynchrony is done by delegation. Here are some examples:


class Program  
{  
  static TimeSpan Boil()  
  {  
    Console.WriteLine(" Kettle: Start boiling water ...");  
    Thread.Sleep(6000);  
    Console.WriteLine(" Kettle: The water is boiling! ");  
    return TimeSpan.MinValue;  
  }  
 
  delegate TimeSpan BoilingDelegate();  
 
  static void Main(string[] args)  
  {  
    Console.WriteLine(" Xiao Wen: Put the kettle on the stove ");  
    BoilingDelegate d = new BoilingDelegate(Boil);  
    IAsyncResult result = d.BeginInvoke(BoilingFinishedCallback, null);  
    Console.WriteLine(" Xiao Wen: Start cleaning up the house ...");  
    for (int i = 0; i < 20; i++)  
    {  
      Console.WriteLine(" Article: Sort out the rules {0} A household ...", i + 1);  
      Thread.Sleep(1000);  
    }  
  }  
 
  static void BoilingFinishedCallback(IAsyncResult result)  
  {  
    AsyncResult asyncResult = (AsyncResult)result;  
    BoilingDelegate del = (BoilingDelegate)asyncResult.AsyncDelegate;
    del.EndInvoke(result);
    Console.WriteLine(" Pour hot water into a thermos ");  
    Console.WriteLine(" Xiao Wen: Keep cleaning up the house ");   
  }  
}

The above example is one of the simplest asynchronous calls, with no argument passing or return validation for the asynchronous call function. This example reflects the process of Xiaowen boiling water. First, Xiaowen puts the kettle on the stove. After defining the delegate, he USES the BeginInvoke method to start the asynchronous call, that is, let the kettle start boiling water. After the water boils, the asynchronous model of C# triggers the callback function specified by the BeginInvoke method, which defines the logic for handling the boiling water, while xiawen pours the water into the thermos and continues to clean up the house.

It can be seen that implementing asynchronous calls in C# is not complicated. First, create an asynchronous handler function and define a delegate for it. Then, when the function is called, the BeginInvoke method of the delegate is used to specify the callback function when the function processing is completed (null can be given a value if the completion event is not needed) and the required parameters (null can be given a value if there are no parameters). Finally, the completion event is handled in the callback function.

Note the EndInvoke call in the above callback function, which causes the calling thread to block until the asynchronous function processing is complete. Obviously, EndInvoke, immediately after BeginInvoke, is used in the same way as a synchronous call.

The return value of the EndInvoke call is the return value of the asynchronous handler. Let's modify the program a little bit and change the Boil method to the following form:


static TimeSpan Boil()  
{  
  DateTime begin = DateTime.Now;  
  Console.WriteLine(" Kettle: Start boiling water ...");  
  Thread.Sleep(6000);  
  Console.WriteLine(" Kettle: The water is boiling! ");  
  return DateTime.Now - begin;  
}  

Then change BoilingFinishedCallback to the following form:


static void BoilingFinishedCallback(IAsyncResult result)  
{  
  AsyncResult asyncResult = (AsyncResult)result;  
  BoilingDelegate del = (BoilingDelegate)asyncResult.AsyncDelegate;  
  Console.WriteLine(" (boil water 1 Share to {0} Time) ", del.EndInvoke(result));  
  Console.WriteLine(" Pour hot water into a thermos ");  
  Console.WriteLine(" Xiao Wen: Keep cleaning up the house ");  
}  

Then we can get the time value returned by the Boil asynchronous handler at EndInvoke. In fact, if the defined BoilingDelegate delegate exists in the argument list, we can also pass the required arguments to the asynchronous handler at BeginInvoke. The signatures of BeginInvoke/EndInvoke functions are related to the delegate signatures that define them.

Note: In the modified BoilingFinishedCallback method, in order to get the delegate instance to get the return value of the asynchronous handler function, we use the following transformation:


AsyncResult asyncResult = (AsyncResult)result;  
BoilingDelegate del = (BoilingDelegate)asyncResult.AsyncDelegate;  

This is how you get the entity that calls the delegate of the asynchronous handler.

.NET handles asynchronous function calls, which are actually done by threads. This process has the following characteristics:

1. The asynchronous function is performed by a thread, the thread in the.NET thread pool

2. Typically, the.NET thread pool has 500 threads (a number that can be set, of course), and whenever BeginInvoke is called to start asynchronous processing, the asynchronous processing function is executed by one of the threads in the pool, without the user having any control over which thread is responsible

3. Because of the limited number of threads in the pool, the new call request causes the function to have to wait for the free thread to appear when the thread in the pool is fully occupied. At this point, the efficiency of the program will be affected.

To verify these features, see the following program:


class Program  
{  
  delegate void MethodInvoker();  
  static void Foo()  
  {  
    int intAvailableThreads, intAvailableIoAsynThreds;  
    ThreadPool.GetAvailableThreads(out intAvailableThreads, out intAvailableIoAsynThreds);  
    string strMessage = String.Format(@"Is Thread Pool: {0},  
    Thread Id: {1} Free Threads {2}",  
        Thread.CurrentThread.IsThreadPoolThread.ToString(),  
        Thread.CurrentThread.GetHashCode(),  
        intAvailableThreads);  
    Console.WriteLine(strMessage);  
    Thread.Sleep(10000);  
    return;  
  }  
 
  static void CallFoo()  
  {  
    MethodInvoker simpleDelegate = new MethodInvoker(Foo);  
    for (int i = 0; i < 15; i++)  
    {  
      simpleDelegate.BeginInvoke(null, null);  
    }  
  }  
  static void Main(string[] args)  
  {  
    ThreadPool.SetMaxThreads(10, 10);  
    CallFoo();  
    Console.ReadLine();  
  }  
}

The program starts with a maximum number of threads in the thread pool of 10, then makes 15 asynchronous calls, each of which takes 10 seconds to process itself. From the execution of the program, we can see that after the current 10 asynchronous calls have fully started, the new asynchronous calls wait (note: the main thread is not waiting) until there are threads in the thread pool that are free.

Hopefully this article has been helpful for your C# programming.


Related articles: