问题:
需要让程序(以异步方式)等待一段时间。
解决方案: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
没有在三秒内完成。