await使用中的阻塞和并发(二)

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

  首先我们比较以下三段代码:

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();
        }
await使用中的阻塞和并发(二)
await使用中的阻塞和并发(二)
        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使用中的阻塞和并发(二)

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

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

await使用中的阻塞和并发(二)
        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()
            };
        }
await使用中的阻塞和并发(二)

  上篇提到Task在创建的时候,就已经开始运行了。而await仅仅是出现在需要等待结果的地方。所以如果无需等待,就不要写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();
        }
await使用中的阻塞和并发(二)

  同时我本人对List<Func<Task>>尚未创建Task却可以并发表示疑问,接下来给出解答。下面分别贴出了使用Lambda和不使用的情况。

await使用中的阻塞和并发(二)
        //使用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造成了阻塞,但并不是在主线程等待,所以我们幸运的并发了……

  再看下面一段,我干脆拿掉了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 _ => _();
        }
await使用中的阻塞和并发(二)

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

await使用中的阻塞和并发(二)
        public void NoAwaitFuncTaskWithoutForeachExtension()
        {
            var funcList = new List<Func<Task>>()
            {
                Delay3000Async,
                Delay2000Async,
                Delay1000Async
            };

            foreach (var func in funcList)
            {
                func();
            }
        }
await使用中的阻塞和并发(二)

  接下来是总结陈述:

  1.   async用于异步,可以优美的替代Thread和Task.Run等方法。
  2.   await用于等待。一定是在你主动希望阻塞并等待返回结果时才使用。
  3.   在Async方法里,Task在创建时就开始运行了。
  4.   无需等待时,async和await不必成对出现。
  5.   写Lamdba别把自己写晕了……

  代码下载

await使用中的阻塞和并发(二)

上一篇:uva 11991 查询中容器的运用


下一篇:position属性absolute与relative(转)