併發系列64章(非同步程式設計)第二章
阿新 • • 發佈:2020-04-07
### 前言
非同步程式設計的概念我在第一章概要的時候,提及了。在此再次簡略概要一次。
```
它採用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,就先到這吧。
下一章,還是幾個例子感受一下。以上為個人理解,如有不對望請