Task.WhenAny取消未完成的任务并超时 [英] Task.WhenAny with cancellation of the non completed tasks and timeout
问题描述
在我的应用中,我正在创建一些并发的Web请求,并且我对其中的任何一个请求完成都感到满意,因此我使用的方法是 Task.WhenAny
:
In my app I am creating some concurrent web requests and I am satisfied when any one of them completes, so I am using the method 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");
首次完成的网址: https://superuser.com
数据:121.954个字符
First Completed Url: https://superuser.com
Data: 121.954 chars
什么我不喜欢这种实现方式,因为未完成的任务会继续下载不再需要的数据,并且浪费带宽,我希望为下一批请求保留这些带宽。因此,我正在考虑取消其他任务,但是我不确定该怎么做。我发现了如何使用 CancellationToken
取消特定的Web请求:
What I don't like to this implementation is that the non-completed tasks continue downloading data I no longer need, and waste bandwidth I would prefer to preserve for my next batch of requests. So I am thinking about cancelling the other tasks, but I am not sure how to do it. I found how to use a CancellationToken
to cancel a specific web request:
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
将使用一组网址,并使用我的 DownloadUrl
函数来获取响应速度最快的网站的数据,并且处理较慢任务的取消逻辑。如果它有一个 timeout 参数,可以为永无止境的任务提供保护,那就太好了。所以我需要这样的东西:
Now I need an implementation of Task.WhenAny
that will take an array of urls, and will use my DownloadUrl
function to fetch the data of the fastest responding site, and will handle the cancellation logic of the slower tasks. It would be nice if it had a timeout argument, to offer protection against never-ending tasks. So I need something like this:
public static Task<Task<TResult>> WhenAnyEx<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, CancellationToken, Task<TResult>> taskFactory,
int timeout)
{
// What to do here?
}
有什么想法吗?
推荐答案
只需将相同的取消令牌传递给所有任务,就像这样:
Simply pass to all of your tasks the same cancellation token, something like this:
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
// here you specify how long you want to wait for task to finish before cancelling
int timeout = 5000;
cts.CancelAfter(timeout);
// pass ct to all your tasks and start them
await Task.WhenAny(/* your tasks here */);
// cancel all tasks
cts.Cancel();
此外,您需要阅读此线程以了解如何使用 CancellationToken
正确:当我使用CancelAfter时(),任务仍在运行
Also, you need to read this thread to be aware of how to use CancellationToken
correctly: When I use CancelAfter(), the Task is still running
这篇关于Task.WhenAny取消未完成的任务并超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!