• 异步编程 async/await


    异步编程可以提高整个系统的响应速度,但不能提高单次请求的时间。

    服务器可以响应更多的请求,每一个请求的总时长基本是不变的。

    当一个请求A处理比较耗时的操作,系统就会不等这个请求A,让A自己处理这个耗时操作,而是直接处理下一个请求B。

    等这个请求A中耗时的操作处理完了,系统再来接着处理请求A后续的功能。

    Async/Await 是一个语法糖,方便写异步编程,其原理是状态机模式编程。

    举了一个点菜的例子:你进一个菜馆,服务员A,来接待你到指定座位,然后你开始点菜,由于你点菜比较耗时,服务员A给了你菜单后就去服务其他客户去了。

    等你好久点完菜了,然后你再叫服务员把菜单给后厨,但这时服务你的不一定是A,很可能是服务员B。

    static async Task Main(string[] args)
            {
                Console.WriteLine("第一:"+Thread.CurrentThread.ManagedThreadId);
                string filePath = @"D:\GitSource\Temp\a\1.txt";
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < 10000; i++)
                {
                    sb.Append("Hello World!");
                }
                Console.WriteLine("第二:" + Thread.CurrentThread.ManagedThreadId);
                await File.WriteAllTextAsync(filePath, sb.ToString());
                Console.WriteLine("第三:" + Thread.CurrentThread.ManagedThreadId);
                string s = await File.ReadAllTextAsync(filePath);
                Console.WriteLine("第四:" + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine(s);
                Console.WriteLine("guala");
                Console.WriteLine("第五:" + Thread.CurrentThread.ManagedThreadId);
                Console.ReadKey();
            }

    可以看到打印出来的线程Id是不一样的。 如果异步处理的时间比较短,也可能一样。

    异步方法返回一般是Task<T>类型(无返回值时是Task),当你用await来等时,取到的结果就是对象本身。类似于Task.Result

    异步不等于多线程

    方法标了Async Task就一定是异步方法,但不一定是多线程方法,比如:

    Console.WriteLine("异步前:"+Thread.CurrentThread.ManagedThreadId);
    double result =await GetResultAsync(5);
    Console.WriteLine("异步后:" + Thread.CurrentThread.ManagedThreadId);
    Console.ReadKey();
    
    async Task<double> GetResultAsync(int input)
    {
        Console.WriteLine("异步中:" + Thread.CurrentThread.ManagedThreadId);
        double result = 0;
        for (int i = 0; i < input * input; i++)
        {
            result += i;
        }
        return result;
        //return await Task.Run( () =>
        //{
        //    double result = 0;
        //    for (int i = 0; i < input * input; i++)
        //    {
        //        result += i;
        //    }
        //     return result;
        //});
    }

    运行结果:

     如果该方法中使用Task来运行:

    可以看出是真的多线程执行。

    线程休息 ,间隔几秒再做某事,用 await Task.Delay(),不阻塞主线程,异步调用,不要用Thread.Sleep(),这个会阻塞主线程 ,影响web服务的性能 

    异步调用时注意参数:CancellationToken ,可以传递参数,适时停止操作

    static async Task Main(string[] args)
            {
                CancellationTokenSource cts = new CancellationTokenSource();
                cts.CancelAfter(1000);//1秒之后取消
                await GetWebSitHtml("http://www.baidu.com", 10, cts.Token);
                Console.ReadKey();
            }
            async static Task GetWebSitHtml(string url,int n,CancellationToken cancellationToken)
            {
                using (HttpClient client = new HttpClient())
                {
                    for (int i = 0; i < n; i++)
                    {
                        string html = await client.GetStringAsync(url);
                        Console.WriteLine(html);
                        if(cancellationToken.IsCancellationRequested)
                        {
                            Console.WriteLine("请求被取消了");
                            break;
                        }
                            
                    }
                }
            }

    如果调用的异步方法本身支持 CancellationToken,则记得要传递该参数给异步方法。

    GetStringAsync 本身不支持 CancellationToken,可以换一个支持的
    async static Task GetWebSitHtml(string url,int n,CancellationToken cancellationToken)
            {
                using (HttpClient client = new HttpClient())
                {
                    for (int i = 0; i < n; i++)
                    {
                        var result = await client.GetAsync(url,cancellationToken);
                        string html =await  result.Content.ReadAsStringAsync();
                        Console.WriteLine(html);
                        if(cancellationToken.IsCancellationRequested)
                        {
                            Console.WriteLine("请求被取消了");
                            break;
                        }
                            
                    }
                }
            }

    微软提供的这个异步方法支持 cancellationToken 参数,我们只需要接过参数传给它就行了。 这个方法里也随时停止。

    再有一种是手动停止:比如输入q字母停止:

    static async Task Main(string[] args)
            {
                CancellationTokenSource cts = new CancellationTokenSource();
                
                GetWebSitHtml("http://www.baidu.com", 100, cts.Token);
                while (Console.ReadLine() !="q")
                {
    
                }            
                cts.Cancel();
                Console.ReadKey();
            }
            async static Task GetWebSitHtml(string url,int n,CancellationToken cancellationToken)
            {
                using (HttpClient client = new HttpClient())
                {
                    for (int i = 0; i < n; i++)
                    {
                        await Task.Delay(1000);//休息一下 要不来不及输入q
                        var result = await client.GetAsync(url,cancellationToken);
                        string html =await  result.Content.ReadAsStringAsync();
                        Console.WriteLine(html);
                        if(cancellationToken.IsCancellationRequested)
                        {
                            Console.WriteLine("请求被取消了");
                            break;
                        }
                            
                    }
                }
            }

      

    杨中科讲异步编程,将的很透彻,耐心看完 .NET 6教程,.Net Core 2022视频教程,杨中科主讲_哔哩哔哩_bilibili

  • 相关阅读:
    hibernate事务管理
    oracle的中文排序问题
    hibernate一级缓存
    Hibernate的实体规则、主键生成策略、对象状态
    【SVN】命令行忽略不必要的文件和文件夹
    【SVN】SVN的trunk、branches、tag的使用以及分支的概念
    hibernate介绍及环境搭建
    敏捷实践:比每日会议更疯狂的半日会议!
    Android开发之有效获取状态栏(StatusBar)高度
    jquery判断输入文字个数的统计代码
  • 原文地址:https://www.cnblogs.com/zhouxiuquan/p/16444796.html
Copyright © 2020-2023  润新知