async,await执行流看不懂?看完这篇以后再也不会了
昨天有朋友在公众号发消息说看不懂await,async执行流,其实看不懂太正常了,因为你没经过社会的毒打,没吃过牢饭就不知道*有多重要,没生过病就不知道健康有多重要,没用过continuewith就不知道await,async有多重要,下面我举两个案例佐证一下?
一:案例一 【嵌套下的异步】
写了这么多年的程序,相信大家都知道连接数据库少不了这几个对象,dbconnection,dbcommand,dbdatareader等等。。先来看看continuewith在连接数据库时嵌套过深的尴尬。
1. netframework 4.0之前的写法
这个时期的代码没有什么好说的,都是程式代码,一撸到底,简洁明了。
public static int syncgetcount() { using (var connection = new mysqlconnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;")) { connection.open(); using (var command = connection.createcommand()) { command.commandtext = "select count(1) from messages"; var count = command.executescalar(); console.writeline($"记录条数:{count}"); return convert.toint32(count); } } } -------- output ------------- 记录条数:75896
2. netframework 4.0下continuewith的写法
当年异步和并发编程概念特别火,火热度参考现在的直播带货,这个时期的c#率先使用新的task一网兜,在数据库操作的几大类中开始有了async结尾的方法,如openasync,executescalarasync,readasync 等等,但遗憾的是那时写异步,只能像下面这样写。
public static task<object> continuewithgetcount() { var connection = new mysqlconnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;"); var task = connection.openasync().continuewith(t1 => { var command = connection.createcommand(); command.commandtext = "select count(1) from messages"; return command.executescalarasync().continuewith(t2 => { command.dispose(); connection.dispose(); console.writeline($"记录条数:{t2.result}"); return t2.result; }); }).unwrap(); return task; } -------- output ------------- 记录条数:75896
相比同步代码,这异步代码写的是不是很憋屈,为了应对渐进式的async方法,我不得不进行continuewith的深层嵌套,如果async更多,那对可读性将是毁灭性的打击,这就是所谓的回调地狱。
3. netframework 4.5 下 await,async的写法
写到这里让我想起了邢老大的那本自传书《左手梦想,右手疗伤》,这苦这心酸只有真正经历过的人才会懂,没有人能够随随便便成功,接下来大家的期望就是如何做到有同步式的代码又有异步功效,鱼和熊掌我都要,当然是可以的,看看如何用await,async进行改造。
public static async task<int> asyncgetcount() { using (var connection = new mysqlconnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;")) { await connection.openasync(); using (var command = connection.createcommand()) { command.commandtext = "select count(1) from messages"; var count = await command.executescalarasync(); console.writeline($"记录条数:{count}"); return convert.toint32(count); } } } -------- output ------------- 记录条数:75896
上面这代码太简洁了,眼花的朋友还以为是同步代码呢? 改造的地方也仅仅是方法签名处加上一个async,异步方法前加上await,相当于痛苦版的continuewith。
二:案例二 【循环下的异步】
上一个案例只是使用executescalarasync从数据库中读取一个值来得到表中的记录数,在业务开发中更多的是使用executereader从数据库中获取批量记录,这个就涉及到了如何在循环中使用异步,想想就太苦难了(┬_┬)。
1. netframework 4.0之前的写法
这里我从messages表中读取5条记录,然后输出到控制台,详细代码如下:
public static list<string> syncgetmessagelist() { var messagelist = new list<string>(); using (var connection = new mysqlconnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;")) { connection.open(); using (var command = connection.createcommand()) { command.commandtext = "select message from messages limit 5;"; using (var reader = command.executereader()) { while (reader.read()) { messagelist.add(reader.getstring("message")); } } } } messagelist.foreach(console.writeline); return messagelist; } ------------- output ---------------- 你需要忘记失去的,感激拥有的,和期待将至的。 以前的找不到了。 对于编译错误,删除pods文件夹然后重新pod install已经成为经验。次。 hello,is there anyone here? 放松心情
2. netframework 4.0下continuewith的写法
要想用continuewith完成这功能,最简单有效的办法就是使用递归,用递归的方式把若干个continuewith串联起来,而要用递归的话还要单独定义一个方法,写的有点乱,大家将就着看吧。
public class program { public static void main(string[] args) { var task = continuewithasyncgetmessagelist(); task.result.foreach(console.writeline); console.read(); } public static task<list<string>> continuewithasyncgetmessagelist() { var connection = new mysqlconnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;"); var task = connection.openasync().continuewith(t1 => { var messagelist = new list<string>(); var command = connection.createcommand(); command.commandtext = "select message from messages limit 5;"; return command.executereaderasync().continuewith(t2 => { var reader = (mysqldatareader)t2.result; return getmessagelist(reader, messagelist).continuewith(t3 => { reader.dispose(); command.dispose(); connection.dispose(); }); }).unwrap().continuewith(t3 => messagelist); }).unwrap(); return task; } /// <summary> /// 采用递归处理循环 /// </summary> /// <param name="reader"></param> /// <param name="messagelist"></param> /// <returns></returns> public static task<list<string>> getmessagelist(mysqldatareader reader, list<string> messagelist) { var task = reader.readasync().continuewith(t => { if (t.result) { var massage = reader.getstring("message"); messagelist.add(massage); return getmessagelist(reader, messagelist); } else { return task.fromresult(new list<string>()); } }).unwrap(); return task; } } ------------ output ---------------- 你需要忘记失去的,感激拥有的,和期待将至的。 以前的找不到了。 对于编译错误,删除pods文件夹然后重新pod install已经成为经验。次。 hello,is there anyone here? 放松心情
在递归下探的过程中把messagelist集合给填满了,而后将messagelist返回给调用端即可,如果没看明白,我画一张图吧!
3. netframework 4.5 下 await,async的写法