与ContinueWith和WhenAll的GetStringAsync - 不等待所有请求完成

时间:2014-03-10 22:11:11

标签: c# .net async-await dotnet-httpclient

我正在尝试处理每个请求,因为它完成了,ContinueWith之后GetStringAsync会发生什么,然后当它们全部完成时会有额外的处理。

但是,似乎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");
    });
}

3 个答案:

答案 0 :(得分:2)

使用ContinueWith时,不应使用TaskContinuationOptions.AttachedToParentasync-await。请仅使用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;
    }
}

答案 1 :(得分:1)

好吧,我发现了这个问题。我会留在这里以防其他人出现寻找类似的答案。我仍然需要await Task.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");
    });
}

答案 2 :(得分:0)

我仍然看到您的解决方案存在两个问题:

  1. 删除使用声明 - you don't want to dispose HttpClient

  2. 删除ContinueWith - 如果您正确使用await,则不需要它们。

  3. this MSDN article中描述的Task.WhenAny方法在完成任务时处理的方式更为清晰。

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

    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");