首页 > 编程语言 >Winform中使用HttpClient(设置最大超时响应时间)调用接口并做业务处理时界面卡住,使用async Task await异步任务编程优化

Winform中使用HttpClient(设置最大超时响应时间)调用接口并做业务处理时界面卡住,使用async Task await异步任务编程优化

时间:2023-03-24 18:13:45浏览次数:73  
标签:异步 Task string await 接口 new async HttpClient httpClient

场景

Winform中怎样使用HttpClient调用http的get和post接口并将接口返回json数据解析为实体类:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/124157296

参考前面使用HttpClient调用http的get和post接口的小示例,

需要定位调用http的get接口并对接口返回数据进行后续处理。

关于定时器的使用在下面文章中有涉及到

Winform中使用mysqldump实现选择部分表定期备份mysql数据库:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/120650090

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi

实现

1、初次业务实现逻辑比较简单,未考虑任务同步执行堵塞UI线程的情况。

在测试时发现,当http接口不通时,而又未设置httpClient的最大超时响应时间,

会导致页面卡死无响应。

优化前的代码:

        private void convertPositionControl() {
            try
            {
                //获取接口数据
                var positionData = requestGetHttpData(positionCalculateUrl);
                //http请求不到数据,啥也不干
                if (null == positionData)
                {
                    return;
                }//请求到数据,则进行数据处理
                else
                {
 
                }
            }
            catch (Exception exception)
            {
                textBox_log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ":获取接口出错:");
                textBox_log.AppendText("\r\n");
                textBox_log.AppendText(exception.Message);
            }
        }

 

上面是在定时器中具体执行的代码,省略部分逻辑,其中请求http接口的方法是

requestGetHttpData,参数getAllBaseStationInfoUrl是接口url。

然后请求接口的方法具体实现是

        private string requestGetHttpData(string apiUrl)
        {
            try {
                //调用接口请求数据
                var originAddressUrl = apiUrl;
                //请求接口数据
                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") + ":调用接口" + apiUrl + "异常:" + exception.Message);
                textBox_log.AppendText("\r\n");
                return null;
            }
        }

这里直接未作任何考虑,只是考虑接口正常的情况。

但是当接口不存在或者报错时就会导致页面卡死。

注意:

HttpClient的预热机制,不要在每次请求接口时都要初始化

httpClient = new HttpClient();

这里放在页面加载完成之后进行初始化一次

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

另外需要给httpClient设置最大响应超时时长

2、C# 中 HttpClient设置最大响应超时时长

 httpClient.Timeout = TimeSpan.FromSeconds(httpClientMaxResponseSeconds);

这里httpClientMaxResponseSeconds是

private double httpClientMaxResponseSeconds = 2;

这里设置为2秒。

后续建议将其优化为单例模式或其他更好的模式。

3、上面卡住问题是因为在同步执行的方法中,请求接口的方法会堵塞UI线程/主线程。

需要将上面请求接口的方法修改成异步任务执行的机制,避免影响UI线程。

这块在之前写mqtt连接时用到到,但是当时不知其所以然。

 

 

Winform中使用MQTTnet实现MQTT的服务端和客户端之间的通信以及将订阅的消息保存到文件:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/117739859

4、异步

同步和异步主要用于修饰方法。当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,

我们称这个方法是同步方法;当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,

调用者不用等待该方法执行完毕,我们称这个方法为异步方法。

异步的好处在于非阻塞(调用线程不会暂停执行去等待子线程完成),因此我们把一些不需要立即使用结果、

较耗时的任务设为异步执行,可以提高程序的运行效率。net4.0在ThreadPool的基础上推出了Task类,

微软极力推荐使用Task来执行异步任务,现在C#类库中的异步方法基本都用到了Task;net5.0推出了async/await,

让异步编程更为方便。

在C#5.0中出现的 async和await ,让异步编程变得更简单。

关于async Task 和await的使用不再详述,具体可自行学习。

下面参考一个网络上的示例

class Program
    {
        static void Main(string[] args)
        {
            string content = GetContentAsync(Environment.CurrentDirectory + @"/test.txt").Result;
            //调用同步方法
            //string content = GetContent(Environment.CurrentDirectory + @"/test.txt");
            Console.WriteLine(content);
            Console.ReadKey();
        }
        //异步读取文件内容
        async static Task<string> GetContentAsync(string filename)
        {
           
            FileStream fs = new FileStream(filename, FileMode.Open);
            var bytes = new byte[fs.Length];
            //ReadAync方法异步读取内容,不阻塞线程
            Console.WriteLine("开始读取文件");
            int len = await fs.ReadAsync(bytes, 0, bytes.Length);
            string result = Encoding.UTF8.GetString(bytes);
            return result;
        }
        //同步读取文件内容
        static string GetContent(string filename)
        {
            FileStream fs = new FileStream(filename, FileMode.Open);
            var bytes = new byte[fs.Length];
            //Read方法同步读取内容,阻塞线程
            int len =  fs.Read(bytes, 0, bytes.Length);
            string result = Encoding.UTF8.GetString(bytes);
            return result;
        }
    }

优化代码

首先将HttpClient修改为单例模式,避免每次请求接口都去new

新建类

 

{

    class Global
    {

        private static string _lockFlag = "GlobalLock";

        private static Global _instance;


        //http请求客户端
        public HttpClient httpClient = new HttpClient();

        private Global()
        {

        }

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

    }
}

关于全局/单例的实现可以参考如下:

C#中全局作用域的常量、字段、属性、方法的定义与使用:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/102550025

这里就一个窗体,所以在窗体初始化后设置其响应时长,也可放在全局工具类的get方法中

        private void Form1_Load(object sender, EventArgs e)
        {
            //设置http连接超时时间
            Global.Instance.httpClient.Timeout = TimeSpan.FromSeconds(httpClientMaxResponseSeconds);
        }

然后改造请求Http接口的方法

        private async Task<string> requestGetHttpData(string apiUrl)
        {
            try {
                var originAddressUrl = apiUrl;
                //请求接口数据        
                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") + ":调用接口" + apiUrl + "异常:" + exception.Message);
                textBox_log.AppendText("\r\n");
                
                return null;
            }
        }

将方法添加async Task<string>,注意这里httpClient调用GetStringAsync方法前面加了await

然后定时器执行中的方法调用修改为

        private async Task convertPositionControl() {
            try
            {
                //获取接口数据
                var positionData = await requestGetHttpData(positionCalculateUrl);
                //http请求不到数据,啥也不干
                if (null == positionData)
                {
                    return;
                }//请求到数据,则进行数据处理
                else
                {
                   
                }
            }
            catch (Exception exception)
            {
                textBox_log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ":获取接口出错:");
                textBox_log.AppendText("\r\n");
                textBox_log.AppendText(exception.Message);
            }
        }

 

至此,不会导致界面卡住的现象。

标签:异步,Task,string,await,接口,new,async,HttpClient,httpClient
From: https://www.cnblogs.com/badaoliumangqizhi/p/17252947.html

相关文章

  • Codeforces 70D. Professor's task
    题目链接:D-Professor'stask题目大意:初始给三个点,之后要求实现两种操作:加点;判断给定点是否在凸包内部。动态凸包板子题,留档怕忘了,参考https://www.cnblogs.com/enzymi......
  • Taskkill
    115outof160ratedthishelpful - ​​Ratethistopic​​Endsoneormoretasksorprocesses.ProcessescanbekilledbyprocessIDorimagename.Syntaxta......
  • Spring线程池ThreadPoolTaskExecutor
    1.线程池配置@ConfigurationpublicclassTaskExecutorConfigimplementsAsyncConfigurer{@Value("${async.core.pool.size:10}")//核心线程数privateIn......
  • Java ThreadPoolTaskExecutor 线程池的常见问题
    JavaThreadPoolTaskExecutor线程池的常见问题 https://blog.csdn.net/weixin_43611528/article/details/123083314 重要参数corePoolSize:核心线程数,常开的线程数,默......
  • 使用spring的@Async异步执行方法
    应用场景:[color=red]1、某些耗时较长的而用户不需要等待该方法的处理结果2、某些耗时较长的方法,后面的程序不需要用到这个方法的处理结果时[/color][size=large][color=......
  • 你不知道的 async、await 魔鬼细节
    0、前言关于promise、async/await的使用相信很多小伙伴都比较熟悉了,但是提到事件循环机制输出结果类似的题目,你敢说都会?试一试?......
  • Android AsyncTask异步任务的使用
    文章目录​​小结​​​​定义异步任务类​​​​开启异步任务​​​​参考​​小结可以使用androidAsyncTask来执行繁重的后台任务,以避免UI界面无响应,并可以实时在UI界面......
  • CF1801G A task for substrings
    题面传送门卡常的出题人什么时候似啊?如果\(l=1,r=|t|\),那么就是蠢得不能再蠢的问题:直接扔到AC自动机上跑匹配就好了,可以做到\(O(\sum|s|+|t|)\)。现在询问的变成了......
  • flutter项目运行时一直卡在Running Gradle task 'assembleDebug'... & Could not reso
    先是看了别人的文章  Flutter项目启动一直卡在RunningGradletask‘assembleDebug‘问题解决-灰信网(软件开发博客聚合)(freesion.com),做了同样的处理,但接着又报错......
  • 启动vagrant up 报错 `await_response_state': scp: /tmp/vagrant-network-entry-eth1
      解决办法Linux df命令用于显示目前在Linux系统上的文件系统的磁盘使用情况统计。Linuxdu命令用于显示目录或文件的大小。du会显示指定的目录或文件所占用的磁盘......