zoukankan      html  css  js  c++  java
  • await使用中的阻塞和并发(二)

    本文继续上篇未完成的讨论,通过将Lambda还原成最普通的代码段,来解释上篇提出的疑问。并更正上篇中一些不太正确的写法。最后会给出无需等待Async方法返回值时,对Async方法使用await的建议,供大家参考。

      首先我们比较以下三段代码,其中第一和第三可以做到并发执行,第二个是线性的执行。

    复制代码
            //并发
            public async Task Await3Task()
            {
                var task3 = Delay3000Async();
                var task2 = Delay2000Async();
                var task1 = Delay1000Async();
    
                await task3;
                await task2;
                await task1;
            }
    
            //非并发
            public async Task Await3DelayAsync()
            {
                await Delay3000Async();
                await Delay2000Async();
                await Delay1000Async();
            }
    
            //并发,这里甚至可以把var task3等去掉,直接调用xxxAsync(),只是会出现警告的波浪罢了
            public void NoAwait3Task()
            {
                var task3 = Delay3000Async();
                var task2 = Delay2000Async();
                var task1 = Delay1000Async();
            }
    复制代码
    复制代码
            //这里补充一下调用的三个Async方法

         public async Task Delay3000Async() { await Task.Delay(3000); Console.WriteLine(3000); Console.WriteLine(DateTime.Now); } public async Task Delay2000Async() { await Task.Delay(2000); Console.WriteLine(2000); Console.WriteLine(DateTime.Now); } public async Task Delay1000Async() { await Task.Delay(1000); Console.WriteLine(1000); Console.WriteLine(DateTime.Now); }
    复制代码

      这里我们可以看出,await和并发木有关系,隐式的并发执行是由async方法决定的。而await是用于主动的阻塞,期望等待方法结束才继续运行时使用。

      以下2个方法的执行结果是一样的,都可以并发执行。可以看出,把仅仅希望并发执行,不需要返回结果的方法丢到List里,然后Foreach是毫无意义的……所以上篇其实是干了一些蠢事情……

    复制代码
            public void AwaitTaskList()
            {
                var tasks = new List<Task>
                {
                    Delay3000Async(),
                    Delay2000Async(),
                    Delay1000Async()
                };
    
                tasks.ForEach(async _ => await _);
            }
    
            public void NoAwaitTaskList()
            {
                var tasks = new List<Task>
                {
                    Delay3000Async(),
                    Delay2000Async(),
                    Delay1000Async()
                };
            }
    复制代码

      上篇提到Task在创建的时候,就已经开始运行了。而await仅仅是出现在需要等待结果的地方。所以如果无需等待,就不要写await了……貌似上篇又干了一些蠢事……

    复制代码
            //非并发
            public async Task Await3Func()
            {
                Func<Task> func3 = Delay3000Async;
                Func<Task> func2 = Delay2000Async;
                Func<Task> func1 = Delay1000Async;
    
                await func3();
                await func2();
                await func1();
            }
            //并发
            public void NoAwait3Func()
            {
                Func<Task> func3 = Delay3000Async;
                Func<Task> func2 = Delay2000Async;
                Func<Task> func1 = Delay1000Async;
    
                func3();
                func2();
                func1();
            }
    复制代码

      同时我本人对List<Func<Task>>尚未创建Task却可以并发表示疑问,接下来给出解答。下面分别贴出了使用Lambda和不使用的情况。我们可以清楚的看到Lambda表达式具体帮我们省略了什么。

    复制代码
            //使用Lambda
            public void AwaitFuncTaskList()
            {
                var funcList = new List<Func<Task>>()
                {
                    Delay3000Async,
                    Delay2000Async,
                    Delay1000Async
                };
    
                funcList.ForEach(async _ => await _());
            }
            //不使用Lambda
            public void AwaitFuncTaskListNoLambda()
            {
                var funcList = new List<Func<Task>>()
                {
                    Delay3000Async,
                    Delay2000Async,
                    Delay1000Async
                };
    
                //funcList.ForEach(AwaitAction());
                foreach(var func in funcList)
                {
                    AwaitAction()(func);
                }
            }
    
            public Action<Func<Task>> AwaitAction()
            {
                return async _ => await _();
            }
    复制代码

      根据上文的总结,可以看出虽然await造成了阻塞,但并不是在主线程等待,所以我们幸运的并发了……

      再看下面一段,我干脆拿掉了await,毫无疑问的并发执行了。上篇让人汗颜的事情貌似还干了不少,好在我脸皮厚,不会删掉前一篇的随笔,哈哈哈哈……

    复制代码
            public void NoAwaitFuncTaskList()
            {
                var funcList = new List<Func<Task>>()
                {
                    Delay3000Async,
                    Delay2000Async,
                    Delay1000Async
                };
    
                funcList.ForEach(_ => _());
            }
    
            public void NoAwaitFuncTaskListNoLambda()
            {
                var funcList = new List<Func<Task>>()
                {
                    Delay3000Async,
                    Delay2000Async,
                    Delay1000Async
                };
    
                //funcList.ForEach(NoAwaitAction());
                foreach(var func in funcList)
                {
                    NoAwaitAction()(func);
                }
            }
    
            public Action<Func<Task>> NoAwaitAction()
            {
                return _ => _();
            }
    复制代码

      仔细看一下可以发现,为了懒惰而使用的ForEach其实增加了多余的一层Action<Func<Task>>,如果直接使用foreach会是如下的情况:

    复制代码
            public void NoAwaitFuncTaskWithoutForeachExtension()
            {
                var funcList = new List<Func<Task>>()
                {
                    Delay3000Async,
                    Delay2000Async,
                    Delay1000Async
                };
    
                foreach (var func in funcList)
                {
                    func();
                }
            }
    复制代码

      接下来是总结陈述:

    1.   async用于异步,可以优美的替代Thread、BackgroundWorker和Task.Run等写法。
    2.   await用于等待。一定是在你主动希望阻塞并等待返回结果时才使用。
    3.   在async方法里,Task在创建时就开始运行了。
    4.   写Lamdba别把自己写晕了……
  • 相关阅读:
    Android AsyncTask
    android 自定义 view 和 ViewGroup
    Android Acitivity 生命周期
    Android Service 与 IntentService
    Android LocalBroadcastManager 与 BroadcastReceiver
    如何提升 service 等级,不被kill(整合)
    Android 插件开发,做成动态加载
    新提交审核app保留检查更新入口将被拒绝(读取App Store 版本号的)
    Android: Service中创建窗口显示
    如何升级PowerShell
  • 原文地址:https://www.cnblogs.com/leo9527/p/10340707.html
Copyright © 2011-2022 走看看