首页 > 学院 > 开发设计 > 正文

批判“await使用中的阻塞和并发”——对asyc/await这对基友的误会和更正

2019-11-17 03:21:17
字体:
来源:转载
供稿:网友

批判“await使用中的阻塞和并发”——对asyc/await这对基友的误会和更正

2014-02-16 16:03 by 楼上那个蜀黍, ... 阅读, ... 评论, 收藏, 编辑

  写第一篇《await使用中的阻塞和并发》的时候还自信满满,觉得写的真不错,结果漏洞百出……

  更正第二篇《await使用中的阻塞和并发(二)》的时候觉得这回不会再错了……

  结果我正在写第三篇,而且连篇名都不敢延用了……

  首先完善第二篇对Foreach(Action<T>)的拆解,用很厉害的小兄弟geelaw的话说就是“是用另一个方法返回λ表达式创建的委托,并未把λ表达式换成方法。”惭愧啊,在小兄弟的指点下,修改代码如下,在Foreach(Action)这个话题上算是圆满了:

        public void AwaitFuncTaskListNoLambda()        {            var funcList = new List<Func<Task>>()            {                Delay3000Async,                Delay2000Async,                Delay1000Async            };             //funcList.ForEach(async _ => await _());            //funcList.ForEach(AwaitAction());            //foreach (var func in funcList)            //{            //    AwaitAction()(func);            //}             Action<Func<Task>> L = this.Xxx;            foreach (var func in funcList)            {                L(func);            }        }                async void Xxx(Func<Task> ft)        {            await ft();        }        public Action<Func<Task>> AwaitAction()        {            return async _ => await _();        }    

  接下来的文字是本篇的重点,来自redjackwang菊苣的指点,他指出了我第二篇中仍存在的误区和错误。下面让我们来领略一下大牛的风采。首先从上篇我给出的结论入手:

  1. async用于异步,可以优美的替代Thread、BackgroundWorker和Task.Run等写法。
  2. await用于等待。一定是在你主动希望阻塞并等待返回结果时才使用。
  3. 在async方法里,Task在创建时就开始运行了。

  第一点需要指出这里的替代仅仅是写法的变化,async方法内部具体实现可能仍使用Thread等多线程技术来实现。这里需要明确async/await仅是一个异步的写法,而Thread/BackgroundWorker和Task是多线程。

  关于第二点,有两位菊苣分别提出异议。redjackwang的原话是“await并不会阻塞任何线程。await只是把当前方法状态保存并立即返回,不管是主线程还是后台线程都不会阻塞,而是在完成时call一个回调。”胖胖的韦恩卑鄙是这么说的:“说await是主动阻塞问题很大。那叫做响应式休眠?

  至于第三点,那是MSDN坑我,redjackwang大人竟然从MSDN上找到了更详细的答案来证明我给出的链接是错误的。在此一定要共享出来给各位,不要被我上篇错误的结论误导了,正确的表述如下:

Tasks created byits publicconstructors are referred to as “cold” tasks, in that they begin their life cycle in the non-scheduled TaskStatus.Created state, and it’s not until Start is called on these instances that they PRogress to being scheduled. All other tasks begin their life cycle in a “hot” state, meaning that their asynchronous execution has already been initiatedand theirTaskStatusis an enumerationvalueotherthan Created.

All tasks returned from TAP methods must be “hot.” If a TAP method internally uses a Task’s constructor to instantiate the task to be returned, the TAP method must call Start on the Task object prior to returning it.Consumers of a TAP methodmaysafelyassume that the returned task is “hot,” andshould not attempt to callStart on any Task returned from a TAP method.Calling Start on a “hot” taskwill result in anInvalidOperationException(this check is handled automatically by the Task class).

  为此redjackwang还给出了证明的例子,在这个例子中,如果注释掉start方法,Task是不会自动运行的。结论是Task如果是通过构造函数创建的,状态是cold的,不会自动运行,而前一篇都是通过返回Task的方法创建出来的,状态是hot,所以自动运行了,和在不在async方法中没有关系。

private async void Button_Click(object sender, RoutedEventArgs e){    var v = await Foo();    this.Title = v.ToString();} private async Task<long> Foo(){    var t = new Task<long>(() =>    {        long z = 0;        for (int i = 0; i < 100000; i++)        {            z += i;        }        return z;    });    //t.Start();    return await t;}

  本篇应不是最终的结果,后面如有进一步的发现仍会更新下去,批判的暴风雨还在继续……


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表