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更多,那對可讀性將是毀滅性的打擊,這就是所謂的回調地獄。async

3. NetFramework 4.5 下 await,async的寫法

寫到這裏讓我想起了邢老大的那本自傳書《左手夢想,右手療傷》,這苦這心酸只有真正經歷過的人才會懂,沒有人可以隨隨便便成功,接下來你們的指望就是如何作到有同步式的代碼又有異步功效,魚和熊掌我都要,固然是能夠的,看看如何用await,async進行改造。code

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。server

二:案例二 【循環下的異步】

上一個案例只是使用ExecuteScalarAsync從數據庫中讀取一個值來獲得表中的記錄數,在業務開發中更多的是使用ExecuteReader從數據庫中獲取批量記錄,這個就涉及到了如何在循環中使用異步,想一想就太苦難了(┬_┬)。對象

1. NetFramework 4.0以前的寫法

這裏我從messages表中讀取5條記錄,而後輸出到控制檯,詳細代碼以下:blog

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的寫法

😄,剛剛是否是噩夢般經歷,救世主來啦,仍是要魚和熊掌一塊兒兼得。

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那種垃圾般代碼所實現的功能,我都想仰天長嘯,我太難了。

三:總結

仍是那句話,你沒有被傷過,永遠不會體會到那種刻骨銘心的痛。


如您有更多問題與我互動,掃描下方進來吧~


相關文章
相關標籤/搜索