一文搞懂c# await,async执行流

作者:一线码农 时间:2023-07-14 01:22:35 

昨天有朋友在公众号发消息说看不懂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返回给调用端即可,如果没看明白,我画一张图吧!

一文搞懂c# await,async执行流

在递归下探的过程中把messageList集合给填满了,而后将messageList返回给调用端即可,如果没看明白,我画一张图吧!

3. NetFramework 4.5 下 await,async的写法

😄,刚刚是不是噩梦般经历,救世主来啦,还是要鱼和熊掌一起兼得


public static async Task<List<string>> AsyncGetMessageList()
{
 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;"))
 {
 await connection.OpenAsync();
 using (var command = connection.CreateCommand())
 {
  command.CommandText = "select message from messages limit 5;";
  using (var reader = await command.ExecuteReaderAsync())
  {
  while (await reader.ReadAsync())
  {
   messageList.Add(reader["message"].ToString());
  }
  }
 }
 }
 return messageList;
}

output

你需要忘记失去的,感激拥有的,和期待将至的。
以前的找不到了。
对于编译错误,删除Pods文件夹然后重新pod install已经成为经验。次。
Hello,Is there anyone here?
放松心情    

天底下还有如此简洁的代码就可以实现ContinueWith那种垃圾般代码所实现的功能,我都想仰天长啸,我太难了。

三:总结

还是那句话,你没有被伤过,永远不会体会到那种刻骨铭心的痛。

来源:https://www.cnblogs.com/huangxincheng/p/12752849.html

标签:c#,await,async,执行流
0
投稿

猜你喜欢

  • Android多渠道打包神器ProductFlavor详解

    2021-06-11 19:48:22
  • JavaWeb利用struts实现文件下载时改变文件名称

    2023-10-24 11:26:53
  • Java spring定时任务详解

    2022-05-22 19:51:06
  • Spring mvc如何实现数据处理

    2023-06-20 09:36:35
  • 浅谈MyBatis通用Mapper实现原理

    2022-11-18 18:45:16
  • C# winform程序读取文本中的值实例讲解

    2023-03-17 21:36:51
  • 用C语言实现五子棋小游戏

    2023-01-19 12:56:58
  • SpringBoot实用小技巧之如何动态设置日志级别

    2023-02-09 04:27:58
  • Java中不可或缺的关键字volatile详析

    2023-07-22 12:11:29
  • C#中WinForm程序退出方法技巧总结

    2022-01-03 12:42:21
  • java如何实现字符串中的字母排序

    2021-09-17 14:55:11
  • .NET/C#实现识别用户访问设备的方法

    2021-12-20 06:30:58
  • 基于WPF实现用户头像选择器的示例代码

    2022-07-07 16:48:18
  • C#聊天程序服务端与客户端完整实例代码

    2023-08-26 18:59:35
  • Android编程之TextView的字符过滤功能分析

    2023-05-19 20:23:36
  • shiro整合springboot前后端分离

    2022-02-22 10:40:31
  • 解决RestTemplate加@Autowired注入不了的问题

    2022-07-14 03:00:48
  • Thymeleaf 3.0 自定义标签方言属性的实例讲解

    2023-03-24 20:40:23
  • string与stringbuilder两者的区别

    2021-11-26 00:01:06
  • Android GSYVideoPlayer视频播放器功能的实现

    2022-01-12 23:57:08
  • asp之家 软件编程 m.aspxhome.com