zoukankan      html  css  js  c++  java
  • 批判“await使用中的阻塞和并发”——对asyc/await这对基友的误会和更正

      写第一篇《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仅是一个异步的写法,而ThreadBackgroundWorker和Task是多线程。

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

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

    Tasks created by its public constructors 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 initiated and their TaskStatus is an enumeration value other than 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 method may safely assume that the returned task is “hot,” and should not attempt to call Start on any Task returned from a TAP method.  Calling Start on a “hot” task will result in an InvalidOperationException (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;
    }

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

     

     

  • 相关阅读:
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    软件工程实践总结
  • 原文地址:https://www.cnblogs.com/manupstairs/p/3551448.html
Copyright © 2011-2022 走看看