如何在可观察的集合上同时完成HTTP调用?

问题描述

在WPF .net核心应用程序中,有以下内容

    @H_404_3@可观察的项目集合(itemObservCollection)。 @H_404_3@静态只读HttpClient _httpclient @H_404_3@ XML响应

我正在对可观察集合中的每个项目(集合中的0到1000个项目)进行api的URL调用。返回的是XML。使用XElement解析XML。可观察集合中的属性值是从XML更新的。

Task.Run用于从UI线程运行操作。 Parallel.Foreach用于在Parallel中进行呼叫。

我觉得我使解决方案过于复杂。有没有一种方法可以简化?单击按钮即可调用UpdateItems()。

private async Task UpdateItems()
{
    try
    {
        await Task.Run(() => Parallel.ForEach(itemObservCollection,new ParallelOptions { MaxDegreeOfParallelism = 12 },async item =>
        {
            try
            {
                var apiRequestString = $"http://localhost:6060/" + item.Name;
                HttpResponseMessage httpResponseMessage = await _httpclient.GetAsync(apiRequestString);
                var httpResponseStream = await httpResponseMessage.Content.ReadAsstreamAsync();
                StringBuilder sb = new StringBuilder(1024);
                XElement doc = XElement.Load(httpResponseStream);
                foreach (var elem in doc.Descendants())
                {
                    if (elem.Name == "ItemDetails")
                    {
                        var itemUpdate = itemObservCollection.FirstOrDefault(updateItem => updateItem.Name == item.Name);
                        if (itemUpdate != null)
                        {
                            itemUpdate.Price = decimal.Parse(elem.Attribute("Price").Value);
                            itemUpdate.Quantity = int.Parse(elem.Attribute("Quantity").Value);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LoggerTextBlock.Text = ('\n' + ex.ToString());
            }
        }));
    }
    catch (Exception ex)
    {
        LoggerTextBlock.Text = ('\n' + ex.ToString());
    }
}

解决方法

您可以创建一系列任务,然后使用Task.WhenAll等待所有任务。

以下示例代码在ObservableCollection<int>中的每个项目上启动了一个任务,然后异步等待所有任务完成:

ObservableCollection<int> itemObservCollection = 
    new ObservableCollection<int>(Enumerable.Range(1,10));

async Task SendAsync()
{
    //query the HTTP API here...
    await Task.Delay(1000);
}

await Task.WhenAll(itemObservCollection.Select(x => SendAsync()).ToArray());

如果要限制并发请求的数量,则可以遍历源收集器的子集以批量发送请求,也可以使用SemaphoreSlim限制实际的并发请求的数量:

Task[] tasks = new Task[itemObservCollection.Count];
using (SemaphoreSlim semaphoreSlim = new SemaphoreSlim(12))
{
    for (int i = 0; i < itemObservCollection.Count; ++i)
    {
        async Task SendAsync()
        {
            //query the HTTP API here...
            try
            {
                await Task.Delay(5000);
            }
            finally
            {
                semaphoreSlim.Release();
            }
        }

        await semaphoreSlim.WaitAsync();
        tasks[i] = SendAsync();
    }
    await Task.WhenAll(tasks);
}