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;
    }

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

     

     

  • 相关阅读:
    体验用yarp连接websocket
    从 ASP.NET Core 5.0 迁移到.NET 6
    对接网易云信音视频2.0呼叫组件集成到vue中,实现web端呼叫app,视频语音通话。
    .NET6 WebAPI 自定义过滤器
    .NET6 WebApi 获取访问者IP地址
    .NET6 部署到 IIS
    .NET6 WebApi JSON传到前台默认变成小驼峰
    开发环境 测试环境 生产环境
    .NET6 WebApi 使用 log4net
    .NET6 WebApi 解决跨域问题
  • 原文地址:https://www.cnblogs.com/manupstairs/p/3551448.html
Copyright © 2011-2022 走看看