首页 > 解决方案 > 如何同时完成对可观察集合的 HTTP 调用?

问题描述

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

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

Task.Run 用于从 UI 线程运行操作。Parallel.Foreach 用于进行并行调用。

我觉得我使解决方案过于复杂。有没有办法简化这个?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());
    }
}

标签: c#wpfasynchronousasync-awaittask-parallel-library

解决方案


您可以创建一组任务并使用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());

如果要限制并发请求的数量,可以遍历源集合的子集以批量发送请求,或者使用 aSemaphoreSlim来限制实际并发请求的数量:

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

推荐阅读