昨天有朋友在公眾號發消息說看不懂await,async執行流,其實看不懂太正常了,因為你沒經過社會的毒打,沒吃過牢飯就不知道自由有多重要,沒生過病就不知道健康有多重要,沒用過ContinueWith就不知道await,async有多重要,下麵我舉兩個案例佐證一下? 一:案例一 【嵌套下的非同步】 寫了 ...
昨天有朋友在公眾號發消息說看不懂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的寫法