如何在可观察集合上并发完成 HTTP 调用?
How to concurrently complete HTTP calls on an observable collection?
在 WPF .net 核心应用程序中有以下内容:
- 项的可观察集合 (itemObservCollection)。
- 静态只读 HttpClient _httpclient
- XML 回应
我正在对可观察集合中的每个项目(集合中的 0 到 1000 个项目)进行 URL 调用 api。 return 是 XML。 XML 使用 XElement 进行解析。可观察集合中的 属性 值是从 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());
}
}
您可以创建一系列任务并等待它们全部使用 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);
}
在 WPF .net 核心应用程序中有以下内容:
- 项的可观察集合 (itemObservCollection)。
- 静态只读 HttpClient _httpclient
- XML 回应
我正在对可观察集合中的每个项目(集合中的 0 到 1000 个项目)进行 URL 调用 api。 return 是 XML。 XML 使用 XElement 进行解析。可观察集合中的 属性 值是从 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());
}
}
您可以创建一系列任务并等待它们全部使用 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);
}