同步請求資源
請求msdn上的一個頁面計算頁面大小
static void Main(string[] args) { string url = "https://docs.microsoft.com/zh-cn/dotnet/core/api/system"; try { WebRequest request = WebRequest.Create(url); WebResponse response = request.GetResponse(); using (var reader = new StreamReader(response.GetResponseStream())) { string text = reader.ReadToEnd(); Console.WriteLine(FormatBytes(text.Length)); } } catch (WebException e) { } catch (IOException e) { } catch (NotSupportedException e) { } }static string FormatBytes(long bytes) { string[] magnitudes = new string[] { "GB", "MB", "KB", "Bytes" }; long max = (long)Math.Pow(1024, magnitudes.Length); var t1 = magnitudes.FirstOrDefault(magnitude => bytes > (max /= 1024)) ?? "0 Bytes"; var t2 = ((decimal)bytes / (decimal)max); return string.Format("{1:##.##} {0}", t1, t2).Trim(); }
Ctrl+F5輸出
閃爍兩下后
這里對資源的請求都是同步的,通俗易懂點就是一個步驟一個步驟的執(zhí)行,任何一個步驟耗時較長都會阻塞上下文線程(這里就是主線程)
使用C#5.0異步請求資源
static void Main(string[] args) { string url = "https://docs.microsoft.com/zh-cn/dotnet/core/api/system"; Task task = WriteWebRequestSizeAsync(url); while (!task.Wait(100)) { Console.Write("."); } }static async Task WriteWebRequestSizeAsync(string url) { try { WebRequest request = WebRequest.Create(url); WebResponse response = await request.GetResponseAsync(); using (var reader = new StreamReader(response.GetResponseStream())) { string text = await reader.ReadToEndAsync(); Console.WriteLine(FormatBytes(text.Length)); } } catch (WebException) { } //省略了一些catch塊}
這種寫法在MVC中早就熟悉了,但是原理確不是很清楚,只知道這樣不會阻塞UI,async方法會創(chuàng)建一個新的線程執(zhí)行,await會阻塞上下文線程,一知半解隱藏著很可怕定時炸彈!
TPL異步調用
static void Main(string[] args) { string url = "https://docs.microsoft.com/zh-cn/dotnet/core/api/system"; Task task = WriteWebRequestSizeAsync(url); try { while (!task.Wait(100)) { Console.Write("."); } } catch (AggregateException excetion) { excetion = excetion.Flatten(); try { excetion.Handle(innerExcetion => { ExceptionDispatchInfo.Capture(excetion.InnerException).Throw(); return true; }); } catch (WebException ex) { } //省略了一些catch塊 } }static Task WriteWebRequestSizeAsync(string url) { StreamReader reader = null; WebRequest request = WebRequest.Create(url); Task task = request.GetResponseAsync() .ContinueWith(antecedent => { WebResponse response = antecedent.Result; reader = new StreamReader(response.GetResponseStream()); return reader.ReadToEndAsync(); }) .Unwrap() .ContinueWith(antecedent => { if (null != reader) reader.Dispose(); string text = antecedent.Result; Console.WriteLine(FormatBytes(text.Length)); }); return task; }
這個寫法是在沒有C#5.0之前,異步請求資源就是這么完成的,乍一看非常復雜,但是比較一下上面一種寫法,它似乎是思路清晰的
1.request.GetResponseAsync創(chuàng)建一個任務等待msdn服務器的響應
2.拿到這個響應后,獲得響應流,將流轉為字符串
3.接下來是Unwrap,這個應該是最難理解的了,實際上只有加上這句話以ContinueWith的思路寫下去,下一步就是直接拿string了
4.最后流已經(jīng)轉為字符串了,我們做個簡單的計算就行了
Unwrap的奧義
public static Task<TResult> Unwrap<TResult>(this Task<Task<TResult>> task);
從簽名上來看,它是一個Task<Task<TResult>>類型的拓展方法,任務,帶返回值的任務...暈了,別急,等下慢慢來
回到剛剛代碼看看有什么端倪
1.GetResponseAsync()創(chuàng)建(意思和返回值一樣,為了區(qū)分任務返回值)一個任務,具體類型Task<WebResponse>,這個任務結束返回一個WebResponse
2.第一個ContinueWith調用者是一個Task<WebResponse>,形參是一個委托接受一個Task<WebReponse>,返回一個Task<string>(通過reader.ReadToEndAsync可以理解),返回值是一個Task沒有問題,問題在它的泛型參數(shù)是什么,首先此處ContinueWith中的代碼是在一個新的工作線程中運行的,我們把它想象成主線程(只是相對的一個環(huán)境),'主線程'要完成什么任務呢?,他要拿Task<WebResponse>的執(zhí)行結果WebResponse(雖然這里可以肯定這個任務已經(jīng)執(zhí)行完成了,但是微軟沒有這么做),然后根據(jù)這個WebResponse在創(chuàng)建一個Task<string>,作為當前工作線程的返回值
3.現(xiàn)在問題來了,我們得到的是一個Task<Task<string>>,我們可以通過調用兩次Result獲取這個string,但是在這個ContinueWith塊中,只能保證外層的Task是執(zhí)行完成的,所以第二個Result或阻塞上下文線程
Task task = request.GetResponseAsync() .ContinueWith(antecedent => { WebResponse response = antecedent.Result; reader = new StreamReader(response.GetResponseStream()); return reader.ReadToEndAsync(); }) .ContinueWith(antecedent => { var resTask = antecedent.Result; var resString = resTask.Result; if (null != reader) { string text = resString; Console.WriteLine(FormatBytes(text.Length)); } });
4.這個時候再回到Unwarp(),它就是脫去了外層的Task,得到的內層的任務上下文線程,并把它作為延續(xù)任務的主線程
5.這里取出的Result就是string,計算并輸出
法器ILSpy
通過ILSpy反編譯后可以逐步查到,Unwarp()實際上就是保證了內層任務開始執(zhí)行,并返回一個內層任務的執(zhí)行環(huán)境(上下文線程)
http://www.cnblogs.com/cheesebar/p/6553310.html