В моем приложении я создаю несколько одновременных веб-запросов, и я удовлетворен, когда любой из них завершается, поэтому я использую метод Task.WhenAny
:
var urls = new string[] {
"https://stackoverflow.com",
"https://superuser.com",
"https://www.reddit.com/r/chess",
};
var tasks = urls.Select(async url =>
{
using (var webClient = new WebClient())
{
return (Url: url, Data: await webClient.DownloadStringTaskAsync(url));
}
}).ToArray();
var firstTask = await Task.WhenAny(tasks);
Console.WriteLine($"First Completed Url: {firstTask.Result.Url}");
Console.WriteLine($"Data: {firstTask.Result.Data.Length:#,0} chars");
Первый завершенный URL: https://superuser.com
Данные: 121,954 символа
Что мне не нравится в этой реализации, так это то, что незавершенные задачи продолжают загружать данные, которые мне больше не нужны, итрата пропускной способности я бы предпочел сохранить для моей следующей партии запросов.Поэтому я думаю об отмене других задач, но я не уверен, как это сделать.Я нашел, как использовать CancellationToken
для отмены определенного веб-запроса:
public static async Task<(string Url, string Data)> DownloadUrl(
string url, CancellationToken cancellationToken)
{
try
{
using (var webClient = new WebClient())
{
cancellationToken.Register(webClient.CancelAsync);
return (url, await webClient.DownloadStringTaskAsync(url));
}
}
catch (WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled)
{
cancellationToken.ThrowIfCancellationRequested();
throw;
}
}
Теперь мне нужна реализация Task.WhenAny
, которая будет принимать массив URL-адресов и будет использовать мою функцию DownloadUrl
извлекать данные самого быстро отвечающего сайта и будет обрабатывать логику отмены более медленных задач.Было бы неплохо, если бы он имел аргумент timeout , чтобы обеспечить защиту от бесконечных задач.Поэтому мне нужно что-то вроде этого:
public static Task<Task<TResult>> WhenAnyEx<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, CancellationToken, Task<TResult>> taskFactory,
int timeout)
{
// What to do here?
}
Есть идеи?