我正在编写一个方法 GetComputers(),它采用 IP 地址字符串列表。我想从这个字符串列表中构建一个具有 IPAddress 和 Name 属性的 Computer 对象列表。要获取名称,我需要向该地址发送 http 请求。因此,我想循环遍历 IP 地址列表,并为每个 IP 地址发送一个使用该地址获取名称的 http 请求。但是,我希望 http 请求能够并行完成。我不想等待每个 http 请求返回才开始处理下一个 ip 地址。我想知道我的方法是否正确。
以下是代码摘录:
电脑班:
public class Computer
{
public string IPAddress {get; set;}
public string Name {get;set;}
}
具有 GetComputers() 方法的类(我已简化它并删除了任何错误处理)
public class MyClass
{
public static HttpClient client = new HttpClient();
public List<Computer> GetComputers(List<string> ipAddresses)
{
List<Computer> computers = new List<Computer>();
List<Task> tasks = new List<Tasks>();
foreach (string ipAddress in ipAddresses)
{
Computer computer = new Computer();
computer.IPAddress = ipAddress;
Task task = Task.Run(() => {
computer.Name = GetName(ipAddress);
});
computers.Add(computer);
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
return computers;
}
private async Task<string> GetName(string ipAddress)
{
HttpResponseMessage response = await client.GetAsync("http://" + ipAddress + "/api/name");
string name = await response.Content.ReadAsStringAsync();
return name;
}
}
到目前为止,这对我有用,但我想知道这是否是 Task.Run() 的正确使用,或者是否有更好的方法
如所呈现的,我很惊讶您的代码可以编译,因为
computer.Name = GetName(ipAddress);
正在将 Task<string>
分配给 string
。即使解决了这个问题,您的方法也存在一些问题,我建议您阅读惯用的异步/等待编程(您通常不应该使用Task.Run
)。
我会推荐这样的东西:
public List<Computer> GetComputers(List<string> ipAddresses) {
return (
await Task.WhenAll(
ipAddresses
.Select(async ipAddress => {
var name = await GetName(ipAddress);
return new Computer {
IPAddress = ipAddress,
Name = name
};
})
)
).ToList();
}
里面有一些有用的技巧。关键是
await Task.WhenAll(things.Select(async thing => await ...))
。 Task.WhenAll
获取 Task
的集合,并将其转换为包含结果的 Task
,因此您只需等待一次。
实际上在幕后发生的事情有点复杂,但本质上
Select
循环遍历您的集合,触发 Task
,但不等待它们完成,然后 await Task.WhenAll(...)
接收它们并等待它们全部,给出最后给你结果。
Google 的其他内容:
IEnumerable
和 Linq(Select
来自哪里)new Computer
语法)