首页 新闻 会员 周边

请教2个关于C# async await 的问题

0
悬赏园豆:20 [待解决问题]

第一个是日常使用方式的问题:

我们一般在项目中经常见到这样的代码:

var result1=await func1Async();

var result2=await func2Async();

var finalResult=await funcFinal(result1,result2);

这种写法其实result2要等result1执行完了才去执行,他们2个是同步执行的了,

写成下边这种才是异步执行的,但很少见有这样写的:

var f1Task = func1Async();
var f2Task = func2Async();
 
var result1= await f1Task ;
var result2= await f2Task ;
var finalResult=await funcFinal(result1,result2);
 
为什么很少见这样的写法呢?
 
第二个问题是关于实现原理的问题,官方文档中写到async await不是在一个新的线程中执行的,
那么它是如何实现异步的呢?(我一直以为是把async方法放到线程池中去运行了,但看文档应该不是我理解的那样)

 

 

就这2个问题,谢谢大家的回答.

hexllo的主页 hexllo | 菜鸟二级 | 园豆:318
提问于:2021-02-18 16:33
< >
分享
所有回答(6)
0

1.可以那种写法,首先很冗余人工劳作,再次可能会出现问题——在你执行创建(并执行)Task2的时候,已经没有机会await Task1了;

await的目的是把并行 执行方便的串行执行,既然串行当然得一个一个来。

2.简单聊一下——

设没有Task,只有Thread,你怎么实现一个类似Task。既然创建Thread开销很大且并发量很大的时候存在很多问题(如数量限制,性能急速下降),首先想到用ThreadPool(内置或者自己实现均可),但仍存在设定的第一个创建Thread开销的问题(首先去用一下Thread才会明白,Thread必须每次新创建)。那么针对Thread的问题,如何解决不就出来了,如容量条件下不干掉Thread,就像你用SqlServerConnection(之所以第二次快,不要以为Colse了,底层模块就完全归零,只是你的代码成归零了而已)。

现在你不得不在构建的这种模式下创建一个通用对象,且叫Task吧;

现在还有多少常用问题需要处理?——传统的Thread并发等待。假设抛开内置已经实现的函数,如何对多个Thread进行等待呢,这是机器擅长的事——不停的往复检查状态即可。但现在你面临的问题已经不是Thread,而是Task,呃,因此面临的问题比较多,比如超出Thread池怎么办,对还需要一种简单的实现单个和多个Task的等待方式(用之前的Thread不得不很麻烦的写很多代码,await就很方便了)。

 

自从移动互联网后特别流行吹概念,比如其他语言的协程~~概念上这玩意儿个人认为可以相当。随便怎么玩,机器的核心就是往复、重复,如果想把计算机械化,用电机也可以驱动。单核没有没有真正意义的并发执行,只有概念上的意义,单片机可能裸跑,没有进程概念,可能直接就是 晶振的Loop 入口。

随便聊聊,希望对你有帮助。

花飘水流兮 | 园豆:13560 (专家六级) | 2021-02-18 19:49

第1个问题中,即使是没有机会await(耗时很少,await时已经执行完成了,await会直接返回结果)也不影响执行嘛,

我尝试把下边代码中的3000改为1,结果也是正常的

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncBreakfast
{
    class Program
    {
        static async Task<string> MethodA()
        {
            await Task.Delay(3000);
            return "A";
        }

        static async Task<string> MethodB()
        {
            await Task.Delay(3000);
            return "B";
        }

        static async Task MainB(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            var aResult = await MethodA();
            var bResult = await MethodB();
            sw.Stop();
            Console.WriteLine(aResult);
            Console.WriteLine(bResult);
            Console.WriteLine($"耗时:{sw.ElapsedMilliseconds} 毫秒.");//6+

            Console.Read();
        }

        static async Task Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            var maTask = MethodA();
            var mbTask = MethodB();

            var aResult = await maTask;
            var bResult = await mbTask;
            sw.Stop();
            Console.WriteLine(aResult);
            Console.WriteLine(bResult);
            Console.WriteLine($"耗时:{sw.ElapsedMilliseconds} 毫秒.");//3+

            Console.Read();
        }

    }
}

 

支持(0) 反对(0) hexllo | 园豆:318 (菜鸟二级) | 2021-02-19 10:50

@hexllo: 自己慢慢品和理解。

支持(0) 反对(0) 花飘水流兮 | 园豆:13560 (专家六级) | 2021-02-19 11:06
0

TPL编程可以看看这篇

智客工坊 | 园豆:1855 (小虾三级) | 2021-02-18 21:10
0

1.你的两种写法,貌似没区别result2要等result1执行完了才去执行,那谁会把代码往多的写呢?
2.【async await不是在一个新的线程中执行的】这个不绝对,也有可能是同一个线程中执行,状态机中的代码分析一下就知道,程序在立刻返回的情况下会选择同步执行,所以有时候也是在同一个线程中执行,这个只是语法糖,当然你说的也对的执行确实通过线程池干的。

56180825 | 园豆:1756 (小虾三级) | 2021-02-19 20:51
0

1。 写法2没有问题。但需要注意,T2 和 T1的结果不能有依赖关系。
2。asyc/await 通常用于IO操作方面。在读写数据时,等待IO完成的时候把控制权交给操作系统,当前线程就会被回收。待IO完成时,操作系统会通知代码继续执。单纯的计算操作,没必须async

pencile | 园豆:845 (小虾三级) | 2021-02-21 18:14
0
  1. 写法没问题,你说的「异步」我猜更多指的是「并行」(和「串行」相反),就是两个 Task 同时开始。至于说少见的原因可能是大家都用 Task.WhenAll() 吧 😃,更多时候可能还是 Fire and Forget。
    如果本身有多个事情在竞争资源意义就不太大了(比如 web server,本身请求就很多,你这边 await 了,线程就可以让给别的请求,不会出现没事干的情况)。

  2. 原文说的是 await 关键字不会创建线程,await 不是创建线程的原因,await 只是会把当前线程让出来。是具体在 await 的方法执行的时候可能会创建线程。

沈星繁 | 园豆:1096 (小虾三级) | 2021-03-04 16:20
0

await TaskA
do something B

我来回答下第二个问题,Async/Await 会在当TaskA完成之后把 do something B 分配给线程池里的线程去执行(.Net Core). 线程池里的线程你不知道是新创建的线程还是已有的线程。

.Net IO异步最关键的是线程池里的IO thread,在你的程序进行IO请求的时候,不需要任何线程进行监听,当IO请求返回结果时会有一个IO thread被call 然后执行do something B,这才真正意义上的异步,有了这个,Async/Await才有意义,而Async/Await不过是一个语法糖,生成一个状态机,在执行到await TaskA的时候就返回一个Task,然后把do something B 变成一个call back action,并且TaskA的网络请求完成的时候,将call back action扔给IO thread 执行,最后Set result in Task.

在没有async/await 之前.net 还有别的方法去利用IO thread实现异步。

这个和Task.Run时完全不同的,Task.Run时并行,Async是异步,但是并行却是异步实现的一种方式。
比起Task.Run, Async的异步是利用最少的线程资源实现异步

Shendu.CC | 园豆:2138 (老鸟四级) | 2021-03-10 16:47
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册