GetStringAsync with ContinueWith和WhenAll——不等待所有请求完成

本文关键字:请求 等待 with ContinueWith WhenAll GetStringAsync | 更新日期: 2023-09-27 17:49:28

我试图处理每个单独的请求,因为它完成了,这是GetStringAsync之后在ContinueWith中会发生的事情,然后当它们都完成了额外的处理。

然而,ContinueWith似乎在WhenAll上立即着火。看起来好像GetStringAsync任务是错误的,但我不知道为什么。

当我使用WaitAll而不是WhenAll并且只是在WaitAll之后添加我的处理时,我的请求工作得很好。但是当我把它改为WhenAll时,它失败了。

下面是我的代码示例:
using (var client = new HttpClient())
{
    Task.WhenAll(services.Select(x =>
    {
        return client.GetStringAsync(x.Url).ContinueWith(response =>
        {
            Console.WriteLine(response.Result);
        }, TaskContinuationOptions.AttachedToParent);
    }).ToArray())
    .ContinueWith(response => 
    {
        Console.WriteLine("All tasks completed");
    });
}

GetStringAsync with ContinueWith和WhenAll——不等待所有请求完成

在使用async-await时不应该使用ContinueWithTaskContinuationOptions.AttachedToParent。只使用async-await,而不是:

async Task<IEnumerable<string>> SomeMethod(...)
{
    using (var client = new HttpClient())
    {
        var ss = await Task.WhenAll(services.Select(async x =>
        {
            var s = await client.GetStringAsync(x.Url);
            Console.WriteLine(response);
            return s;
        };
        Console.WriteLine("All tasks completed");
        return ss;
    }
}

我发现了问题。我把它留在这里,以防有人来找类似的答案。我仍然需要awaitTask.WhenAll方法。

所以,正确的代码应该是:
using (var client = new HttpClient())
{
    await Task.WhenAll(services.Select(x =>
    {
        return client.GetStringAsync(x.Url).ContinueWith(response =>
        {
            Console.WriteLine(response.Result);
        }, TaskContinuationOptions.AttachedToParent);
    }).ToArray())
    .ContinueWith(response => 
    {
        Console.WriteLine("All tasks completed");
    });
}

我仍然看到你的解决方案的几个问题:

  1. 删除using语句-你不想处理HttpClient

  2. 删除ContinueWith s -如果你正确使用await,你不需要它们

  3. 这篇MSDN文章中描述的Task.WhenAny方法是在任务完成时处理任务的一种更干净的方法。

我会重写你的例子,像这样:

var client = new HttpClient();
var tasks = services.Select(x => client.GetStringAsync(x.Url)).ToList();
while (tasks.Count > 0)
{
    var firstDone = await Task.WhenAny(tasks);
    tasks.Remove(firstDone);
    Console.WriteLine(await firstDone);
}
Console.WriteLine("All tasks completed");

编辑地址注释:

如果您需要在任务完成时访问service对象,一种方法是将tasks修改为Task<ObjectWithMoreData>而不是Task<string>的列表。注意,lambda被标记为async,因此我们可以在其中添加await:

var client = new HttpClient();
var tasks = services.Select(async x => new 
{
    Service = x,
    ResponseText = await client.GetStringAsync(x.Url)
}).ToList();
while (tasks.Count > 0) 
{
    var firstDone = await Task.WhenAny(tasks);
    tasks.Remove(firstDone);
    var result = await firstDone;
    Console.WriteLine(result.ResponseText);
    // do something with result.Service
}
Console.WriteLine("All tasks completed");