Task.WhenAny取消未完成的任务并超时 [英] Task.WhenAny with cancellation of the non completed tasks and timeout

查看:629
本文介绍了Task.WhenAny取消未完成的任务并超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用中,我正在创建一些并发的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆