《C#并发编程经典实例》学习笔记—2.5 等待任意一个任务完成Task.WhenAny

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

《C#并发编程经典实例》学习笔记—2.5 等待任意⼀个任务完
成Task.WhenAny
问题
执⾏若⼲个任务,只需要对其中任意⼀个的完成进⾏响应。

这主要⽤于:对⼀个操作进⾏多种独⽴的尝试,只要⼀个尝试完成,任务就算完成。

例如,同时向多个 Web 服务询问股票价格,但是只关⼼第⼀个响应的。

⽂中举的是向多个Web服务询问股票价格的例⼦。

我曾在过往的⼯作中遇到另⼀个不太相似的例⼦。

⼀个问答项⽬,在问题详情页⾯,重要的是问题展⽰和回答展⽰。

在该页⾯有相关房型推荐和类似问题推荐等等多个模块展⽰。

也就是说在请求问题数据之外还需要请求多个接⼝,按理说这个时候最适合的是使⽤Task.WhenAll,但是当时情形下因为服务器性能受限导致页⾯加载过慢影响⽤户访问,所以其时最快需要解决的是页⾯加载过慢的问题,所以这时使⽤Task.WhenAny或许也算得上是⼀个应急折中的⽅案,当然这⾥不提缓存等其他优化⽅案。

Task.WhenAny与Task.WhenAll⽐较:
相同点:参数都是⼀批任务
不同点:Task.WhenAny返回的是完成的任务。

关于返回值的描述有点不太好理解。

结合代码很容易就能明⽩。

// 返回第⼀个响应的 URL 的数据长度。

private static async Task<int> FirstRespondingUrlAsync(string urlA, string urlB)
{
var httpClient = new HttpClient();
// 并发地开始两个下载任务。

Task<byte[]> downloadTaskA = httpClient.GetByteArrayAsync(urlA);
Task<byte[]> downloadTaskB = httpClient.GetByteArrayAsync(urlB);
// 等待任意⼀个任务完成。

Task<byte[]> completedTask = await Task.WhenAny(downloadTaskA, downloadTaskB);
// 返回从 URL 得到的数据的长度。

byte[] data = await completedTask;
return data.Length;
}
注意 Task<byte[]> completedTask = await Task.WhenAny(downloadTaskA, downloadTaskB);,使⽤await获取返回结果仍然是⼀个Task任务,如果Task.WhenAny返回的Task有异常,这⾏代码并不会抛出异常,⽽是在byte[] data = await completedTask;这⾥抛出异常。

在第⼀个任务完成之后,如果其他任务没有被取消,也不曾await,那么这些任务将被遗弃,被遗弃的任务并不是代表任务停⽌,⽽是任务继续执⾏直到完成,当然这些被遗弃的任务的结果或异常都会被忽略。

⽂中提到了另外两个对WhenAny的使⽤⽅法。

我试着写了demo。

使⽤Task.WhenAny实现超时功能
书中给出的思路是其中⼀个任务是Delay的,这样返回的第⼀个任务如果是该Delay任务。

⽂中不推荐该⽅式,因为没有取消功能,即其他超时的任务不能被取消,⽆疑对计算机资源是⼀种浪费,对性能也会造成影响。

// 返回第⼀个响应的 URL 的数据长度。

private static async Task<int> FirstRespondingUrlAsync(string urlA, string urlB)
{
var httpClient = new HttpClient();
// 并发地开始两个下载任务。

Task<byte[]> downloadTaskA = httpClient.GetByteArrayAsync(urlA);
Task<byte[]> downloadTaskB = httpClient.GetByteArrayAsync(urlB);
Task<byte[]> delayTask = GetDelayTask();
// 等待任意⼀个任务完成。

Task<byte[]> completedTask = await Task.WhenAny(downloadTaskA, downloadTaskB, delayTask);
// 返回从 URL 得到的数据的长度。

byte[] data = await completedTask;
if (data.Length == 1 && data[0] == byte.MaxValue)
{
Console.WriteLine("超时提醒");
}
return data.Length;
}
// 获取超时任务
private static Task<byte[]> GetDelayTask()
{
return new Task<byte[]>(() =>
{
Task.Delay(1000);
return new[] { byte.MaxValue };
});
}
使⽤Task.WhenAny处理已完成的任务
书中给出的思路是,列表存放Task,完成⼀个任务就移除⼀个已完成的Task。

⽂中不推荐此⽅法,因为执⾏时间是O(N^2),2.6⼩节有O(N)的算法。

// 处理已完成的任务
private static async Task ProcessTasksAsync(string urlA, string urlB)
{
var httpClient = new HttpClient();
// 并发地开始两个下载任务。

Task<byte[]> downloadTaskA = httpClient.GetByteArrayAsync(urlA);
Task<byte[]> downloadTaskB = httpClient.GetByteArrayAsync(urlB);
var tasks = new List<Task<byte[]>> { downloadTaskA, downloadTaskB };
while (true)
{
// 等待任意⼀个任务完成。

Task<byte[]> completedTask = await Task.WhenAny(tasks);
//移除已完成的任务
tasks.Remove(completedTask);
if (!tasks.Any())
{
break;
}
}
}。

相关文档
最新文档