zoukankan      html  css  js  c++  java
  • 你眼中的async/await是什么样的?(转)

    对async/await的理解问题。让我们直奔主题:

    1
    2
    3
    4
    5
    6
    7
    8
    var foodsSearch = new FoodSearchService().SearchAsync();
    var fruitsSearch = new FruitSearchService().SearchAsync();
     
    var foods = await foodsSearch;
     
    foods.ForEach(f => Console.WriteLine("food:{0}", f.Name));
     
    Console.WriteLine("done");

    这是一段使用async/await的代码,算得上是async/await的最佳使用实践。问题出在大家对这段代码理解各有不同,让我们来看看如何理解这段代码:

    这个理解正确吗?团队的争论焦点在步骤2上,大家错误的认为await关键字就是等待的意思,为了在第三步拿到结果,线程在步骤2处等待直到FoodSearchService返回结果。如果你也是这样理解的,那么你应该看看下面的代码如何理解?

    1
    2
    3
    4
    5
    6
    7
    8
    var foodsSearch = new FoodSearchService().SearchAsync();
    var fruitsSearch = new FruitSearchService().SearchAsync();
     
    var foods = foodsSearch.Result;
     
    foods.ForEach(f => Console.WriteLine("food:{0}", f.Name));
     
    Console.WriteLine("done");

    刚才错误的理解正好是这段代码的描述。那么第一段使用async/await的代码如何理解?

    使用await标记的方法调用(上面例子中的foodsSearch)不会阻塞主线程,主线程在步骤2不会等待。

    为了说明这个结论,我们用下面的代码模拟await(仅仅是模拟其行为,并不能真确执行),我没有研究过await的实现,但是下面的代码跟await具有相同的行为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var foodsSearch = new FoodSearchService().SearchAsync();
    var fruitsSearch = new FruitSearchService().SearchAsync();
     
    var callback = foodsSearch as ICallBackRegister;
    callback.Register<List<Food>>(foods =>
    {
        foods.ForEach(f => Console.WriteLine("food:{0}", f.Name));
        Console.WriteLine("done");
    }).Await(foodsSearch);

    为了方便大家理解,我们写一个简单的ICallBackRegister实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class CallbackRegister:ICallBackRegister
    {
        Action<object> _action;
        public ICallBackRegister Register<T>(Action<T> callback)
        {
            _action = o => callback((T) o);
            return this;
        }
     
        public void Await(Task task)
        {
            task.ContinueWith((result)=>_action(result));
        }
    }

    从上面模拟的代码中我们可以得出两个结论:

    1、没有任何阻塞主线程的代码。

    2、尽量推迟await的调用,能在步骤2使用await就不要在步骤1使用。因为一旦使用了await,后面所有的代码都变成了await所调用对象的回调,无法跟之前的异步代码并行。即便你在步骤1就使用了await,只能说FoodSearchService和FruitSearchService两者不能并行,但是任然不会阻塞主线程——在主线程上永远没有等待这一说。

    我们再看张图来解释一下这期间发生的事情:

    设想这样的代码放在一个GUI中Button的click事件中,由于await不会阻塞主线程,界面再不会有假死的情况发生。

    1
    2
    3
    4
    5
    6
    7
    8
    this.BackColor = Color.Aquamarine;
    this.btnAsyncAwait.BackColor = Color.Blue;
     
    var operationA = new LongTimeOperationA().GetValueAsync();
    var operationB = new LongTimeOperationB().GetValueAsync();
     
    var valueA =await operationA;
    Text = valueA;

    同样的道理,在web mvc编程中,如果controller和EF中全程使用async/await,此时假设用户有一个请求过来,IIS会从线程池中取出一个线程来响应用户请求,由于主线程没有任何阻塞,所以IIS会很快将线程回收到了线程池中。当EF返回数据并且返回ActionResult时,IIS再次从线程池中拿出一个线程来对用户请求做响应。所以

    正是由于async/await不会阻塞主线程,我们才说async/await会提高IIS的响应能力。

    另外async/await的使用并不会提升访问数据库的效率,该花多长时间还得花多长时间。

    最后我们给出Task.Result版本的click事件,由于调用Task.Result会阻塞主线程,所以你可以看到界面假死的现象。

    1
    2
    3
    4
    5
    6
    7
    8
    this.BackColor = Color.Beige;
    this.btnTaskResult.BackColor = Color.BlueViolet;
     
    var operationA=new LongTimeOperationA().GetValueAsync();
    var operationB=new LongTimeOperationB().GetValueAsync();
     
    var valueA = operationA.Result;
    Text = valueA;

    代码下载:download

    作者:Richie Zhang
    声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
     
  • 相关阅读:
    学习官方示例 System.Assigned
    用 TBytesStream 类实现的读文件为十六进制字符的函数
    学习官方示例 System.Hi、System.Lo
    给 TComboBox 添加图标 回复 "heyongan" 的问题
    [每周特惠]WPF编程(第二版) + 设计模式—基于C#的工程化实现及扩展
    【公告】8月28日(周六)早上5:007:00服务器升级
    【网站公告】新Web服务器上线
    欢迎参加上海张江浦东软件园以及分园的技能培训课程
    上周热点回顾(8.309.5)
    上周热点回顾(8.168.22)
  • 原文地址:https://www.cnblogs.com/shw0099/p/7193042.html
Copyright © 2011-2022 走看看