异步编程 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

posted @ 2022-07-04 23:05  百年俊少  阅读(96)  评论(0编辑  收藏  举报