問題:
需要讓程式(以非同步方式)等待一段時間。
解決方案:Task類的靜態函式Delay,返回Task物件
在github開源專案dotnet/coreclr
,找到Task.cs有關Delay方法的原始碼
github地址:
https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
/// <summary>
/// Creates a Task that will complete after a time delay.
/// </summary>
/// <param name="millisecondsDelay">The number of milliseconds to wait before completing the returned Task</param>
/// <returns>A Task that represents the time delay</returns>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// The <paramref name="millisecondsDelay"/> is less than -1.
/// </exception>
/// <remarks>
/// After the specified time delay, the Task is completed in RanToCompletion state.
/// </remarks>
public static Task Delay(int millisecondsDelay)
{
return Delay(millisecondsDelay, default);
}
Delay方法會建立一個延遲millisecondsDelay
毫秒後完成的任務。
millisecondsDelay
為 在完成返回的任務前要等待的毫秒數,如果值為-1,將無限期等待。
Delay方法有多個過載方法,如下
public static Task Delay(TimeSpan delay);
public static Task Delay(TimeSpan delay, CancellationToken cancellationToken);
public static Task Delay(int millisecondsDelay);
public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken);
書中給出三個例子。
一個是單元測試中,定義一個非同步完成的任務,以完成“非同步成功”測試。
static async Task<T> DelayResult<T>(T result, TimeSpan delay)
{
await Task.Delay(delay);
return result;
}
一個是指數退避的簡單實現。
指數退避是一種重試策略,重試的延遲時間會逐次增加。在訪問 Web 服務時,最好的方式就是採用指數退避,它可以防止伺服器被太多的重試阻塞。
書中提到實際產品開發中,應對指數退避重試機制有更周密的解決方案。書中推薦了微軟企業庫中的瞬間錯誤處理模組(Transient Error Handling Block),在微軟Docs中找到了相關文章。
暫時性故障處理 (構建使用 Azure 的真實世界雲應用程式):
https://docs.microsoft.com/zh-cn/aspnet/aspnet/overview/developing-apps-with-windows-azure/building-real-world-cloud-apps-with-windows-azure/transient-fault-handling
static async Task<string> DownloadStringWithRetries(string uri)
{
using (var client = new HttpClient())
{
// 第 1 次重試前等 1 秒,第 2 次等 2 秒,第 3 次等 4 秒。
var nextDelay = TimeSpan.FromSeconds(1);
for (var i = 0; i != 3; ++i)
{
try
{
return await client.GetStringAsync(uri);
}
catch
{
}
await Task.Delay(nextDelay);
nextDelay = nextDelay + nextDelay;
}
// 最後重試一次,以便讓呼叫者知道出錯資訊。
return await client.GetStringAsync(uri);
}
}
上述程式碼實現的是對非同步get請求進行多次重試。
最後一個例子,是實現了一個簡單的超時功能,當get請求在3秒內沒有響應,返回null。
static async Task<string> DownloadStringWithTimeout(string uri)
{
using (var client = new HttpClient())
{
var downloadTask = client.GetStringAsync(uri);
var timeoutTask = Task.Delay(3000);
var completedTask = await Task.WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
}
}
該程式碼的實現主要是藉助於Task.WhenAny
方法。
Task.WhenAny
的返回值是提供的多個任務中已完成的任務。
如果已完成的任務completedTask
和timeoutTask
相等,證明最先完成的任務是等待三秒之後完成的任務timeoutTask
,也就是說downloadTask
沒有在三秒內完成。