c# - Parallel.ForEach 与 HttpClient 和 ContinueWith

标签 c# asynchronous task-parallel-library async-await dotnet-httpclient

我有一个方法尝试从多个 URL 中并行下载数据,并返回反序列化类型的 IEnumerable

方法如下所示:

    public IEnumerable<TContent> DownloadContentFromUrls(IEnumerable<string> urls)
    {
        var list = new List<TContent>();

        Parallel.ForEach(urls, url =>
        {
            lock (list)
            {
                _httpClient.GetAsync(url).ContinueWith(request =>
                {
                    var response = request.Result;
                    //todo ensure success?

                    response.Content.ReadAsStringAsync().ContinueWith(text =>
                    {
                        var results = JObject.Parse(text.Result)
                            .ToObject<IEnumerable<TContent>>();

                        list.AddRange(results);
                    });
                });
            }
        });

        return list;
    }

在我的单元测试中(我将 _httpClient stub 以返回一组已知的文本)我基本上得到了

Sequence contains no elements

这是因为该方法在任务完成之前返回。

如果我在 .ContinueWith() 调用的末尾添加 .Wait() ,它会通过,但我确定我在这里滥用了 API...

最佳答案

如果你想要一个使用 HttpClient.GetAsync 方法并行下载的阻塞调用,那么你应该像这样实现它:

public IEnumerable<TContent> DownloadContentFromUrls<TContent>(IEnumerable<string> urls)
{
    var queue = new ConcurrentQueue<TContent>();

    using (var client = new HttpClient())
    {
        Task.WaitAll(urls.Select(url =>
        {
            return client.GetAsync(url).ContinueWith(response =>
            {
                var content = JsonConvert.DeserializeObject<IEnumerable<TContent>>(response.Result.Content.ReadAsStringAsync().Result);

                foreach (var c in content)
                    queue.Enqueue(c);
            });
        }).ToArray());
    }

    return queue;
}

这将创建一组任务,每个 Url 一个,代表一个 GetAsync/Deserialize 操作。这是假设 Url 返回 TContent 的 Json 数组。空数组或单个成员数组可以很好地反序列化,但单个无数组对象则不行。

关于c# - Parallel.ForEach 与 HttpClient 和 ContinueWith,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19383910/

相关文章:

c# - 多次比较字符串

javascript - 在 if 语句中使用 child_process.exec 的返回值?

c# - ASP.NET Core 2.0 身份,2 个 Controller 分别重定向到不同的登录页面

c# - 无法通过 ASP.NET Web 应用程序将数据插入 MySQL 表

Java 同步 - 我做对了吗?

javascript - PhantomJS javascript 等待函数完成

c# - ArgumentOutOfRangeException 使用任务

c# - 如何等到一系列任务完成?

c# - 如何在不等待的情况下安全地调用 C# 中的异步方法

c# - BlockingCollection 或 Queue<T> 用于作业?