1. 程式人生 > >併發系列64章(非同步程式設計)第二章

併發系列64章(非同步程式設計)第二章

### 前言 非同步程式設計的概念我在第一章概要的時候,提及了。在此再次簡略概要一次。 ``` 它採用future模式或者回調模式機制,以避免產生不必要的執行緒。 ``` ### 非同步程式設計測試的標準 在第一個寫這個的原因,是因為測試可能比開發重要。因為在開發一個專案的時候呢?有一個自動化高效精準測試,決定了上線是否穩定。因為程式出bug測試出來可以改,方案不行換方案,但是測試不行上線了。這時候面臨的問題就比較大,因為這時候產生了資料。 比如說 app 一張表的設計不合理,在自動化測試中沒有體現出來,那麼你要更換表的時候就顯得異常困難,這時候到底換不換表的結構呢?換了之後,如何相容之前的版本?迭代的方案是啥。好的,扯得很遠了。 當然我們作為開發人員也要做好單元測試,及子系統測試。好的,近了一點了。 我們在寫一個非同步程式的時候,是有3個測試必須通過。 1.同步成功 2.非同步成功 3.非同步失敗 先介紹一下如何非同步測試: ``` csharp public static async Task DelayResult(T result, TimeSpan delay) { await Task.Delay(delay); return result; } ``` 如何測試的時候如果這樣寫: ``` csharp [Fact] public async void Test1() { TimeSpan timeSpan = new TimeSpan(); Program.DelayResult(1, timeSpan); } ``` 那麼這個測試是有問題的。 比如: ``` csharp public static async Task DelayResult(T result, TimeSpan delay) { await Task.Delay(delay); throw new Exception("error"); return result; } ``` 本來我是應該丟擲異常的,但是: ![](https://img2020.cnblogs.com/blog/1289794/202004/1289794-20200403185638664-2054461022.png) 結果是下面這樣的。 原因就涉及到一個異常捕獲的問題了,可以查詢一下原理。 執行測試的時候應該加上await: ``` csharp [Fact] public async void Test1() { TimeSpan timeSpan = new TimeSpan(); await Program.DelayResult(1, timeSpan); } ``` ![](https://img2020.cnblogs.com/blog/1289794/202004/1289794-20200403185955098-1760617658.png) 那麼這個時候就可以捕獲到異常。 下面介紹一些例子。 ### 指數退避 這個是什麼意思呢?比如說,我們訪問我們的一條url的時候,訪問失敗。 接下來我們應該做的是重試,那麼是否馬上重試?不是的,除非是阻塞式的api呼叫,例如登入。 但是呢,如果不是阻塞式的,那麼應該把資源分配均衡。因為你一次失敗,第二次的也有可能失敗。 那麼這時候指數退避是一種良好的方法。 ``` csharp static async Task visitUrl(string url) { using (var client = new HttpClient()) { var nextDelay = TimeSpan.FromSeconds(1); for (int i = 0; i != 3; ++i) { try { return await client.GetStringAsync(url); } catch { } await Task.Delay(nextDelay); nextDelay = nextDelay + nextDelay; } // 返回最後的結果方便得出錯誤 return await client.GetStringAsync(url); } } ``` 測試: ``` csharp [Fact] public async void Test1() { await Program.visitUrl("www.xxx.com"); } ``` 結果: ![](https://img2020.cnblogs.com/blog/1289794/202004/1289794-20200407165708745-340123284.png) 測試花了7秒。 正確驗證測試我就不測了。 ### 實現超時功能 上面的這個程式碼,我們發現一個問題啊,如果訪問那個連結要好久,那麼這也很受傷啊。 是否能加入一個超時,如果訪問一段時間沒有返回結果,那麼把資源留給別的需求者。 ``` csharp public static async Task visitTimeoutUrl(HttpClient client,string url) { var visitTask=client.GetStringAsync(url); var timeoutTask = Task.Delay(3000); var completedTask = await Task.WhenAny(visitTask,timeoutTask); if (completedTask == timeoutTask) { return null; } return await visitTask; } ``` 上文實現了一個簡單的超時。 然後改一下: ``` csharp public static async Task visitUrl(string url) { using (var client = new HttpClient()) { var nextDelay = TimeSpan.FromSeconds(1); for (int i = 0; i != 3; ++i) { try { var result= await visitTimeoutUrl(client,url); if (result != null) { return result; } } catch { } await Task.Delay(nextDelay); nextDelay = nextDelay + nextDelay; } // 返回最後的結果方便得出錯誤 return await visitTimeoutUrl(client, url); } } ``` ### 未完 今天寫部落格的時候,一直出現error,就先到這吧。 下一章,還是幾個例子感受一下。以上為個人理解,如有不對望請