併發系列64章(異步編程)第二章

前言

異步編程的概念我在第一章概要的時候,說起了。在此再次簡略概要一次。編程

它採用future模式或者回調模式機制,以免產生沒必要要的線程。

異步編程測試的標準

在第一個寫這個的緣由,是由於測試可能比開發重要。由於在開發一個項目的時候呢?有一個自動化高效精準測試,決定了上線是否穩定。由於程序出bug測試出來能夠改,方案不行換方案,可是測試不行上線了。這時候面臨的問題就比較大,由於這時候產生了數據。api

好比說 app 一張表的設計不合理,在自動化測試中沒有體現出來,那麼你要更換表的時候就顯得異常困難,這時候到底換不換表的結構呢?換了以後,如何兼容以前的版本?迭代的方案是啥。好的,扯得很遠了。app

固然咱們做爲開發人員也要作好單元測試,及子系統測試。好的,近了一點了。異步

咱們在寫一個異步程序的時候,是有3個測試必須經過。async

1.同步成功異步編程

2.異步成功單元測試

3.異步失敗測試

先介紹一下如何異步測試:url

public static async Task<T> DelayResult<T>(T result, TimeSpan delay)
{
	await Task.Delay(delay);
	return result;
}

如何測試的時候若是這樣寫:線程

[Fact]
public async void Test1()
{
	TimeSpan timeSpan = new TimeSpan();
	Program.DelayResult<int>(1, timeSpan);
}

那麼這個測試是有問題的。
好比:

public static async Task<T> DelayResult<T>(T result, TimeSpan delay)
{
	await Task.Delay(delay);
	throw new Exception("error");
	return result;
}

原本我是應該拋出異常的,可是:

結果是下面這樣的。
緣由就涉及到一個異常捕獲的問題了,能夠查詢一下原理。
運行測試的時候應該加上await:

[Fact]
public async void Test1()
{
	TimeSpan timeSpan = new TimeSpan();
	await Program.DelayResult<int>(1, timeSpan);
}


那麼這個時候就能夠捕獲到異常。

下面介紹一些例子。

指數退避

這個是什麼意思呢?好比說,咱們訪問咱們的一條url的時候,訪問失敗。
接下來咱們應該作的是重試,那麼是否立刻重試?不是的,除非是阻塞式的api調用,例如登陸。
可是呢,若是不是阻塞式的,那麼應該把資源分配均衡。由於你一次失敗,第二次的也有可能失敗。
那麼這時候指數退避是一種良好的方法。

static async Task<string> 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);
	}
}

測試:

[Fact]
public async void Test1()
{
	await Program.visitUrl("www.xxx.com");
}

結果:

測試花了7秒。
正確驗證測試我就不測了。

實現超時功能

上面的這個代碼,咱們發現一個問題啊,若是訪問那個連接要很久,那麼這也很受傷啊。
是否能加入一個超時,若是訪問一段時間沒有返回結果,那麼把資源留給別的需求者。

public static async Task<string> 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;
}

上文實現了一個簡單的超時。
而後改一下:

public static async Task<string> 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,就先到這吧。 下一章,仍是幾個例子感覺一下。以上爲我的理解,若有不對望請指出。

相關文章
相關標籤/搜索