Asynchronous calls to delegates and methods

Overview

Asynchronous calls are usually used to perform some time-consuming operations, while ensuring that the current main thread can continue to execute after executing the asynchronous call. During asynchronous execution, the system often creates a new thread, but it should be noted that when each asynchronous execution requires the creation of a thread, performance will often be affected. Therefore, using a thread pool can effectively handle this problem and avoid the overhead of creating and destroying threads when making asynchronous calls.

This article combines delegation and asynchronous to do a simple study of the BeginInvoke() method, EndInvoke() method and their related IAsyncResult.

Example 1: Not using asynchronous calls

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Client application started!\\
");
            Console.WriteLine(DateTime.Now.ToString());
            Thread.CurrentThread.Name = "Main Thread";
            Calculator cal = new Calculator();
            int result = cal.Add(2, 5);
            Console.WriteLine("Result: {0}\\
", result);

            // Do some other things, the simulation takes 3 seconds to execute
            for (int i = 1; i <= 3; i + + )
            {
                Thread.Sleep(TimeSpan.FromSeconds(i));
                Console.WriteLine("{0}: Client executed {1} second(s).",
                        Thread.CurrentThread.Name, i);
            }
            Console.WriteLine(DateTime.Now.ToString());
            Console.WriteLine("\\
Press any key to exit...");
            Console.ReadKey();

        }

    }
    public class Calculator
    {
        public int Add(int x, int y)
        {
            //Determine whether the thread executing the current code is a thread in the thread pool
            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Thread.CurrentThread.Name = "Pool Thread";
            }
            Console.WriteLine("Method invoked!");

            //Perform something, the simulation takes 2 seconds to execute
            for (int i = 1; i <= 2; i + + )
            {
                Thread.Sleep(TimeSpan.FromSeconds(i));
                Console.WriteLine("{0}: Add executed {1} second(s).",
                        Thread.CurrentThread.Name, i);
            }
            Console.WriteLine("Method complete!");
            return x + y;
        }
    }
}

As can be seen from the above figure, when asynchronous execution is not used, the main thread executes for about 6 seconds because the threads are executed serially.

Next, define a delegate and use the BeginInvoke() method to call it asynchronously

Example 2: BeginInvoke() method call

1. Let’s look at the code example first

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    //Declaration of delegation
    public delegate int AddDelegate(int x, int y);
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Client application started!\\
");
            Console.WriteLine(DateTime.Now.ToString());
            Thread.CurrentThread.Name = "Main Thread";
            Calculator cal = new Calculator();

            AddDelegate del=new AddDelegate(cal.Add);
            IAsyncResult result=del.BeginInvoke(3,4,null,null);

            // Do some other things, the simulation takes 3 seconds to execute
            for (int i = 1; i <= 3; i + + )
            {
                Thread.Sleep(TimeSpan.FromSeconds(i));
                Console.WriteLine("{0}: Client executed {1} second(s).",
                        Thread.CurrentThread.Name, i);
            }
            Console.WriteLine(DateTime.Now.ToString());
            Console.WriteLine("\\
Press any key to exit...");
            Console.ReadKey();

        }

    }
    public class Calculator
    {
        public int Add(int x, int y)
        {
            //Determine whether the thread executing the current code is a thread in the thread pool
            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Thread.CurrentThread.Name = "Pool Thread";
            }
            Console.WriteLine("Method invoked!");

            //Perform something, the simulation takes 2 seconds to execute
            for (int i = 1; i <= 2; i + + )
            {
                Thread.Sleep(TimeSpan.FromSeconds(i));
                Console.WriteLine("{0}: Add executed {1} second(s).",
                        Thread.CurrentThread.Name, i);
            }
            Console.WriteLine("Method complete!");
            return x + y;
        }
    }
}

①BeginInvoke() method parameters

BeginInvoke() except that the last two parameters are AsyncCallback type and Object type, the previous parameter types and numbers are the same as the delegate definition.

AsyncCallback parameters

AsyncCallback is a delegate type, which is used for method callbacks, that is, a method that is automatically called when the asynchronous method completes execution.

Object type parameters

The Object type is used to pass any desired data type, which can be obtained through the AsyncState property of IAsyncResult

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    //Declaration of delegation
    public delegate int AddDelegate(int x, int y);
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Client application started!\\
");
            Console.WriteLine(DateTime.Now.ToString());
            Thread.CurrentThread.Name = "Main Thread";
            Calculator cal = new Calculator();

            AddDelegate del=new AddDelegate(cal.Add);
            AsyncCallback async = new AsyncCallback(OnAddComplete);
            string data = "The value passed as the last parameter when calling BeginInvoke() was obtained in the OnAddComplete() method";

            IAsyncResult result=del.BeginInvoke(3,4,async,data);
            

            // Do some other things, the simulation takes 3 seconds to execute
            for (int i = 1; i <= 3; i + + )
            {
                Thread.Sleep(TimeSpan.FromSeconds(i));
                Console.WriteLine("{0}: Client executed {1} second(s).",
                        Thread.CurrentThread.Name, i);
            }
            
            Console.WriteLine(DateTime.Now.ToString());
            Console.WriteLine("\\
Press any key to exit...");
            Console.ReadKey();

        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="asyncResult">There is no need to save IAsyncResult after calling BeginInvoke(), because the AsyncCallback delegate defines the object in the parameter list of the callback method;</param>
        static void OnAddComplete(IAsyncResult asyncResult)
        {
            AsyncResult result = (AsyncResult)asyncResult;
            AddDelegate del = (AddDelegate)result.AsyncDelegate;
            string data = (string)asyncResult.AsyncState;

            int rtn = del.EndInvoke(asyncResult);
            Console.WriteLine("{0}: Result, {1}; Data: {2}\\
",
                    Thread.CurrentThread.Name, rtn, data);
        }
    }
    public class Calculator
    {
        public int Add(int x, int y)
        {
            //Determine whether the thread executing the current code is a thread in the thread pool
            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Thread.CurrentThread.Name = "Pool Thread";
            }
            Console.WriteLine("Method invoked!");

            //Perform something, the simulation takes 2 seconds to execute
            for (int i = 1; i <= 2; i + + )
            {
                Thread.Sleep(TimeSpan.FromSeconds(i));
                Console.WriteLine("{0}: Add executed {1} second(s).",
                        Thread.CurrentThread.Name, i);
            }
            Console.WriteLine("Method complete!");
            return x + y;
        }
    }
}

②BeginInvoke() method result return type

The BeginInvoke() method returns an object that implements the IAsyncResult interface.

AsyncResult has several uses: passing parameters, which contains a reference to the delegate that called BeginInvoke(); it also contains the last Object type parameter of BeginInvoke(); it can identify which method is used at which time Call, because the same method can be called multiple times through the same delegate variable

③EndInvoke() method

The EndInvoke() method accepts an object of type IAsyncResult, so after calling BeginInvoke(), you need to retain the IAsyncResult so that it can be passed when EndInvoke() is called. The most important thing here is the return value of the EndInvoke() method, which is the return value of the method. In addition, When the client calls EndInvoke(), if the asynchronously called method has not been completed, the current thread will be interrupted and wait for the method. It will only continue when the asynchronous method is completed. Execute the following code. So it makes no sense to execute EndInvoke() immediately after calling BeginInvoke(). We usually call BeginInvoke() as early as possible, and then call EndInvoke() when the return value of the method is needed, or call it later depending on the situation.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    //Declaration of delegation
    public delegate int AddDelegate(int x, int y);
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Client application started!\\
");
            Console.WriteLine(DateTime.Now.ToString());
            Thread.CurrentThread.Name = "Main Thread";
            Calculator cal = new Calculator();

            AddDelegate del=new AddDelegate(cal.Add);
            IAsyncResult result=del.BeginInvoke(3,4,null,null);

            // Do some other things, the simulation takes 3 seconds to execute
            for (int i = 1; i <= 3; i + + )
            {
                Thread.Sleep(TimeSpan.FromSeconds(i));
                Console.WriteLine("{0}: Client executed {1} second(s).",
                        Thread.CurrentThread.Name, i);
            }

            var rtn=del.EndInvoke(result);
            Console.WriteLine("Getting results: {0}\\
", rtn);
            Console.WriteLine(DateTime.Now.ToString());
            Console.WriteLine("\\
Press any key to exit...");
            Console.ReadKey();

        }

    }
    public class Calculator
    {
        public int Add(int x, int y)
        {
            //Determine whether the thread executing the current code is a thread in the thread pool
            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Thread.CurrentThread.Name = "Pool Thread";
            }
            Console.WriteLine("Method invoked!");

            //Perform something, the simulation takes 2 seconds to execute
            for (int i = 1; i <= 2; i + + )
            {
                Thread.Sleep(TimeSpan.FromSeconds(i));
                Console.WriteLine("{0}: Add executed {1} second(s).",
                        Thread.CurrentThread.Name, i);
            }
            Console.WriteLine("Method complete!");
            return x + y;
        }
    }
}