Multithreading and asynchronous programming

Asynchronous callback function:

For the definition of online search, please refer to:

Author: no.body
Link: https://www.zhihu.com/question/19801131/answer/27459821
Source: Zhihu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.
 

What is a callback function?

Let's go a long way to answer this question.

Programming is divided into two categories: System programming (system programming) and application programming. The so-called system programming, in short, is to write a library; Application programming is to use various libraries to write programs with certain functions, that is, applications. System programmers will leave some interfaces for their own libraries, namely API (application programming interface), Application programming interface )For application programmers. Therefore, in the diagram of the abstraction layer, the library is located below the application.

When the program runs, generally, application program The application program will often call the functions prepared in advance in the library through the API. However, some library function s require the application to pass a function to it first, so that it can be called at an appropriate time to complete the target task. This function that is passed in and then called is called a callback function( callback function).

For example, a hotel provides wake-up service, but passengers are required to decide the method of wake-up. You can call the guest room or send a waiter to knock on the door. If you sleep so hard that you are afraid of delaying things, you can also ask to pour a basin of water on your head. Here, the "wake-up" behavior is provided by the hotel, which is equivalent to the library function, but the wake-up method is determined by the passenger and told the hotel, that is, the callback function. The action of passengers telling the hotel how to wake themselves up, that is, the action of transferring the callback function to the storage function, is called to register a callback function. As shown in the figure below (picture source: Wikipedia):

As you can see, the callback function is usually at the same abstraction level as the application (because what callback function is passed in is determined at the application level). The callback becomes a high-level call to the bottom, and the bottom goes back to call the high-level process. (I think) this should be the earliest application of callback and the reason why it is so named.

Advantages of callback mechanism

As can be seen from the above example, the callback mechanism provides great flexibility. Please note that from now on, we will change the library function in the figure to intermediate function, because callback is not only used between application and library. Any time you want flexibility similar to the above, you can take advantage of callbacks.

How is this flexibility achieved? At first glance, the callback seems to be just a call between functions, but after careful consideration, we can find a key difference between the two: in the callback, we use some way to pass the callback function into the intermediate function like a parameter. It can be understood that the intermediate function is incomplete before passing in a callback function. In other words, the program can determine and change the behavior of the intermediate function by registering different callback functions at run time. This is much more flexible than simple function calls. See the following simple example of a callback written in Python:

`even.py`

#Callback function 1
#Generate an even number of 2k form
def double(x):
    return x * 2
    
#Callback function 2
#Generate an even number of 4k form
def quadruple(x):
    return x * 4

`callback_demo.py`

from even import *

#Intermediate function
#Accepts a function that generates an even number as an argument
#Returns an odd number
def getOddNumber(k, getEvenNumber):
    return 1 + getEvenNumber(k)
    
#Starting function, here is the main function of the program
def main():    
    k = 1
    #When it is necessary to generate an odd number in the form of 2k+1
    i = getOddNumber(k, double)
    print(i)
    #When an odd number in the form of 4k+1 is required
    i = getOddNumber(k, quadruple)
    print(i)
    #When an odd number in the form of 8k+1 is required
    i = getOddNumber(k, lambda x: x * 8)
    print(i)
    
if __name__ == "__main__":
    main()

Run` callback_demp.py `, the output is as follows:

3
5
9

In the above code, different callback functions are passed to 'getOddNumber', and their performance is also different. This is the advantage of the callback mechanism. It is worth mentioning that the third callback function above is a Anonymous function.

Easily overlooked third parties

It can be seen from the above discussion that intermediate function and callback function are two necessary parts of callback, but people often ignore the third important role in callback, that is, the caller of intermediate function. In most cases, the caller can be equivalent to the main function of the program, but for the sake of difference, I call it the starting function here (as shown in the comments in the above code).

The reason why this third party is specially emphasized is that I get an impression when reading relevant articles on the Internet. Many people simply understand it as the back and forth call between two individuals. For example, when explaining "callback", many Chinese web pages will mention the following sentence: "If you call me, I will call you back." I can't find the source of this English sentence. I personally guess that many people regard the start function and callback function as one for two reasons: first, it may be the misleading name of "callback"; Second, what kind of callback function is passed to the intermediate function is determined in the starting function. In fact, the callback is not the interaction between "you and me", but the tripartite linkage of ABC. With this clear concept, it is not easy to confuse and make mistakes when implementing callbacks in your own code.

In addition, there are actually two kinds of callbacks: blocking callbacks and deferred callbacks. The difference between the two is: in a blocking callback, the call of the callback function must occur before the return of the starting function; In the delayed callback, the callback function may be called after the return of the starting function. We do not intend to discuss these two probabilities in more depth here. The reason why they are proposed is to illustrate the importance of emphasizing the starting function. When many articles on the Internet mention these two concepts, they just generally say that blocking callback occurs in calling function Before returning, it is not clear whether the main calling function is the starting function or the intermediate function, which is confusing, so it is specially explained here. Also note that the examples in this article are blocking callbacks. Deferred callbacks usually involve multithreading. I haven't fully understood it myself, so I won't talk about it here.

*********************************************Routine**********************************************************

Let's talk about the parameters of the callback function first:

BeginInvoke (< input and output variables >, asynccallback, object asyncstate)

Parameter 1: represents the method argument corresponding to the delegate

Parameter 2: callback, callback function, which indicates the function automatically called at the end of asynchronous call.

Parameter 3: asyncState is used to provide relevant parameters to the callback function.

Return value: IAsyncResult -- > asynchronous operation status interface, which encapsulates parameters in asynchronous execution.

The invoke () method of the delegate type continuously queries whether the asynchronous call ends with the help of the IAsyncResult interface object.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AsyncDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //Synchronous programming
        private void btnTest1_Click(object sender, EventArgs e)
        {
            this.lblInfo1.Text = ExecuteTask1(10).ToString();
            this.lblInfo2.Text = ExecuteTask2(20).ToString();
        }

        //Asynchronous call
        private void btnTest2_Click(object sender, EventArgs e)
        {
            //Assign values to delegate variables
            MyCalculator objMyCal = ExecuteTask1;
            //Using delegate variables
            IAsyncResult result = objMyCal.BeginInvoke(10,null,null);
            this.lblInfo1.Text = "Calculating, please wait............";
            //Perform other tasks
            this.lblInfo2.Text = ExecuteTask2(20).ToString();
            //Get the result of asynchronous execution
            int r = objMyCal.EndInvoke(result);
            this.lblInfo1.Text = r.ToString();
        }

        //Declare delegate variables
        private int ExecuteTask1(int num)
        {
            System.Threading.Thread.Sleep(5000);
            return num * num;
        }
        private int ExecuteTask2(int num)
        {
            return num * num;
        }
    }

    //Define delegate type
    public delegate int MyCalculator(int num);
}

Another form:

 //[1] Declare a delegate type
    public delegate int MyCalculator(int num,int ms);
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            //[4] Assign values to delegate variables
            this.objMyCal = ExecuteTask;
            //You can also use Lambda expressions
            this.objMyCal = (num,ms) => {
                System.Threading.Thread.Sleep(ms);
                return num * num;
            };
        }
        //[3] Define delegate variables
        private MyCalculator objMyCal = null;

        //[5] Call delegate
        private void btnExec_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 11; i++)
            {
                //Here are 11 threads
                objMyCal.BeginInvoke(10*i,1000*i,MyCallBack,i);
                //The last parameter i assigns a value to the field AsyncState of the callback function. If there is a lot of data, it can be defined as a class or structure
            }
        }

        //Callback function
        private void MyCallBack(IAsyncResult result)
        {
            int res = objMyCal.EndInvoke(result);
            //Asynchronous result display: result The asyncstate field is used to encapsulate user-defined parameters during callback, object type
            Console.WriteLine("The first{0}The calculation results are:{1}",result.AsyncState.ToString(),res);
        }
        //[2] Define method according to delegate
        private int ExecuteTask(int num,int ms)
        {
            System.Threading.Thread.Sleep(ms);
            return num * num;
        }

       
    }

Note / / the last parameter i assigns a value to the AsyncState field of the callback function. If there is a lot of data, it can be defined as a class or structure

Keywords: C#

Added by NoPHPPhD on Mon, 17 Jan 2022 05:58:25 +0200