前言
上次的《如何为 Task 添加超时功能》文章中,我们的实现使用了循环判断,等待任务执行完成,在性能上会有一定影响。
有网友留言,提供了一个更好的实现思路:.
为什么说这种方法更好呢?
原理
官网上有一篇文章《使用 Async 和 Await 的异步编程》[1],介绍了如何高效地等待任务,其中有这样一段话:
另一种选择是使用 WhenAny,它将返回一个当其参数完成时才完成的 Task。你可以等待返回的任务,了解它已经完成了。以下代码展示了可以如何使用 WhenAny 等待第一个任务完成,然后再处理其结果。
var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == eggsTask)
{
Console.WriteLine("Eggs are ready");
}
else if (finishedTask == baconTask)
{
Console.WriteLine("Bacon is ready");
}
else if (finishedTask == toastTask)
{
Console.WriteLine("Toast is ready");
}
breakfastTasks.Remove(finishedTask);
}
也就是说,Task.WhenAny
会返回其参数中第一个完成的任务,而无需循环判断。
根据以上描述,我们可以按如下方式修改超时功能:
-
同时开启 2 个任务,一个原始 task,另一个是刚好在超时时间完成的 timeoutTask
-
使用 WhenAny 等待
-
检查返回值是否为 timeoutTask,如果是就表示原任务超时了
实现
修改后的 Timeout 扩展方法:
public static async Task<T> Timeout<T>(this Task<T> task, int milliseconds)
{
var timeoutTask = Task.Delay(milliseconds);
var allTasks = new List<Task> { task, timeoutTask };
Task finishedTask = await Task.WhenAny(allTasks);
if (finishedTask == timeoutTask)
{
return default(T);
}
return await task;
}
使用方式还是保持不变:
var data = await GetFromCache().Timeout(1000);
if (data is null)
{
data = await GetFromDB();
}
结论
今天,我们通过使用 Task.WhenAny
,为异步任务添加了超时功能。
在此感谢网友“受气de灰太狼”提供的解决思路!