When using HttpClient (setting the maximum timeout response time) in Winform to call the interface and do business processing, the interface is stuck, and the async task await is used for asynchronous task programming optimization

Scene

How to use HttpClient in Winform to call the get and post interfaces of http and parse the json data returned by the interface into entity classes:

How to use HttpClient in Winform to call the get and post interfaces of http and parse the json data returned by the interface into entity classes_winform requests http interface_Overbearing rogue temperament blog-CSDN blog

Refer to the previous small example of using HttpClient to call the get and post interfaces of http,

It is necessary to locate the get interface that calls http and perform subsequent processing on the data returned by the interface.

The use of timers is covered in the following articles

Use mysqldump in Winform to select some tables to back up the mysql database regularly:

Use mysqldump in Winform to select some tables to back up the mysql database regularly

Note:

blog:
Domineering rogue temperament blog

Implementation

1. The initial business implementation logic is relatively simple, without considering the situation that the task synchronous execution blocks the UI thread.

During the test, it was found that when the http interface is unreachable and the maximum timeout response time of httpClient is not set,

It will cause the page to freeze and become unresponsive.

Code before optimization:

 private void convertPositionControl() {
            try
            {
                //Get interface data
                var positionData = requestGetHttpData(positionCalculateUrl);
                //http can't request data, do nothing
                if (null == positionData)
                {
                    return;
                }//Request data, then data processing
                else
                {
 
                }
            }
            catch (Exception exception)
            {
                textBox_log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ":Error getting interface:");
                textBox_log.AppendText("\r\\
");
                textBox_log.AppendText(exception.Message);
            }
        }

The above is the specific code executed in the timer, omitting some logic, and the method of requesting the http interface is

requestGetHttpData, the parameter getAllBaseStationInfoUrl is the interface url.

Then the specific implementation of the request interface method is

 private string requestGetHttpData(string apiUrl)
        {
            try {
                //Call the interface to request data
                var originAddressUrl = apiUrl;
                //Request interface data
                if (null == httpClient) {
                    httpClient = new HttpClient();
                }
                var url = new Uri(originAddressUrl);
                var response = httpClient. GetAsync(url). Result;
                var data = response.Content.ReadAsStringAsync().Result;
                return data;
            }
            catch (Exception exception) {
                textBox_log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ": call interface" + apiUrl + "Exception:" + exception.Message);
                textBox_log.AppendText("\r\\
");
                return null;
            }
        }

There is no direct consideration here, only the normal situation of the interface is considered.

But when the interface does not exist or an error is reported, the page will be stuck.

Notice:

The warm-up mechanism of HttpClient, do not initialize every time the interface is requested

httpClient = new HttpClient();

Here it is initialized once after the page is loaded

 private void Form1_Load(object sender, EventArgs e)
        {
            httpClient = new HttpClient();
            httpClient.Timeout = TimeSpan.FromSeconds(httpClientMaxResponseSeconds);
        }

In addition, you need to set the maximum response timeout period for httpClient

2. HttpClient in C# sets the maximum response timeout period

 httpClient.Timeout = TimeSpan.FromSeconds(httpClientMaxResponseSeconds);

Here httpClientMaxResponseSeconds is

private double httpClientMaxResponseSeconds = 2;

Here it is set to 2 seconds.

Subsequent suggestions optimize it to a singleton mode or other better modes.

3. The above stuck problem is because in the method of synchronous execution, the method of requesting the interface will block the UI thread/main thread.

It is necessary to modify the above method of requesting the interface into a mechanism for asynchronous task execution to avoid affecting the UI thread.

This piece was used when writing the mqtt connection before, but I didn’t know why at the time.

MQTTnet is used in Winform to realize the communication between the MQTT server and the client and to save the subscribed messages to the file:

MQTTnet is used in Winform to realize the communication between MQTT server and client and to save subscribed messages to files_mqtt winform_Overbearing rogue temperament blog-CSDN blog

4. Asynchronous

Synchronous and asynchronous are mainly used to decorate methods. When a method is called, the caller needs to wait for the method to complete and return before continuing.

We call this method a synchronous method; when a method is called, it returns immediately and obtains a thread to execute the business inside the method,

The caller does not have to wait for the method to finish executing, we call this method an asynchronous method.

The advantage of asynchrony is that it is non-blocking (the calling thread will not suspend execution to wait for the child thread to complete), so we put some results that do not need to be used immediately,

Time-consuming tasks are set to be executed asynchronously, which can improve the running efficiency of the program. net4.0 launched the Task class on the basis of ThreadPool,

Microsoft strongly recommends the use of Task to execute asynchronous tasks. Now the asynchronous methods in the C# class library basically use Task; net5.0 introduced async/await,

Make asynchronous programming more convenient.

The async and await that appeared in C# 5.0 make asynchronous programming easier.

The use of async Task and await will not be described in detail, and you can learn by yourself.

Refer to an example on the network below

class Program
    {
        static void Main(string[] args)
        {
            string content = GetContentAsync(Environment.CurrentDirectory + @"/test.txt").Result;
            //Call the synchronous method
            //string content = GetContent(Environment.CurrentDirectory + @"/test.txt");
            Console. WriteLine(content);
            Console. ReadKey();
        }
        // read file content asynchronously
        async static Task<string> GetContentAsync(string filename)
        {
           
            FileStream fs = new FileStream(filename, FileMode. Open);
            var bytes = new byte[fs. Length];
            //The ReadAync method reads the content asynchronously without blocking the thread
            Console.WriteLine("Start reading file");
            int len = await fs. ReadAsync(bytes, 0, bytes. Length);
            string result = Encoding.UTF8.GetString(bytes);
            return result;
        }
        //Synchronously read the contents of the file
        static string GetContent(string filename)
        {
            FileStream fs = new FileStream(filename, FileMode. Open);
            var bytes = new byte[fs. Length];
            //The Read method reads the content synchronously, blocking the thread
            int len = fs. Read(bytes, 0, bytes. Length);
            string result = Encoding.UTF8.GetString(bytes);
            return result;
        }
    }

Optimize code

First, modify HttpClient to singleton mode to avoid going to new every time the interface is requested

new class

{

    class Global
    {

        private static string _lockFlag = "GlobalLock";

        private static Global _instance;


        //http request client
        public HttpClient httpClient = new HttpClient();

        private Global()
        {

        }

        public static Global Instance
        {
            get
            {
                lock (_lockFlag)
                {
                    if (_instance == null)
                    {
                        _instance = new Global();
                    }
                    return_instance;
                }
            }
        }

    }
}

The implementation of the global/singleton can refer to the following:

Definition and use of constants, fields, attributes, and methods in global scope in C#:

Definition and use of constants, fields, attributes, and methods in global scope in C# – Programmer Sought

Here is a form, so set its response time after the form is initialized, or it can be placed in the get method of the global tool class

 private void Form1_Load(object sender, EventArgs e)
        {
            //Set the http connection timeout
            Global.Instance.httpClient.Timeout = TimeSpan.FromSeconds(httpClientMaxResponseSeconds);
        }

Then transform the method of requesting the Http interface

 private async Task<string> requestGetHttpData(string apiUrl)
        {
            try {
                var originAddressUrl = apiUrl;
                //Request interface data
                var url = new Uri(originAddressUrl);
                string jsonResponse = await Global.Instance.httpClient.GetStringAsync(url);
                return jsonResponse;
            }
            catch (Exception exception) {

                textBox_log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ": call interface" + apiUrl + "Exception:" + exception.Message);
                textBox_log.AppendText("\r\\
");
                
                return null;
            }
        }

Add the method to async Task, note that here httpClient calls the GetStringAsync method before adding await

Then the method call in the timer execution is modified to

 private async Task convertPositionControl() {
            try
            {
                //Get interface data
                var positionData = await requestGetHttpData(positionCalculateUrl);
                //http can't request data, do nothing
                if (null == positionData)
                {
                    return;
                }//Request data, then data processing
                else
                {
                   
                }
            }
            catch (Exception exception)
            {
                textBox_log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ":Error getting interface:");
                textBox_log.AppendText("\r\\
");
                textBox_log.AppendText(exception.Message);
            }
        }

So far, the interface will not be stuck.