访问 foreach 中 LINQ Select 中使用的值

Accessing value used in LINQ Select within a foreach

给定 IP 地址列表:

List<string> ipList = new List<string>(); //example: 192.168.0.1, 192.168.0.2, 192.168.0.3 etc.

我试图以并行方式遍历列表中的每个 IP,然后在屏幕上打印一条有意义的消息:

foreach (PingReply pingReply in ipList.AsParallel().WithDegreeOfParallelism(64).Select(ip => new Ping().Send(ip)))
{
    Console.WriteLine($"Ping status: {pingReply.Status} for the target IP address: {ip}");
}

我无法在该上下文中访问 ip。我真的很想了解如何访问每个 relative ip 当我发送它们时?

我已经探索了 PingReply object but PingReply.Address 作为示例包含主机(发件人)IP,因此它无法满足此要求。我真的希望 PingReply 对象包含被 ping 的 Ip!


更新

根据@haim770 和@MindSwipe 提供的示例,我最终使用了:

foreach (var pingResponseData in ipList.AsParallel().WithDegreeOfParallelism(64).Select(ip => new { ip, pingReply = new Ping().Send(ip) }))
{
    Console.WriteLine($"Ping status: {pingResponseData.pingReply.Status} for the target IP address: {pingResponseData.ip}");
}

更新 2

根据@pinkfloydx33 关于使用 ValueTuple 的评论,我已按照以下示例完成:

foreach (var (ip, reply) in ipList.AsParallel().WithDegreeOfParallelism(ipList.Count).Select(ip => (ip, new Ping().Send(ip, 150))))
{
    Console.WriteLine($"Ping status: {reply.Status} for the target IP address: {ip}");
}

您目前只selectpingReply,而不是ippingReply,为此您需要select 一个新的 anonymous type 并对其进行迭代。像这样:

foreach (var (pingReply, ip) in ipList.AsParallel().WithDegreeOfParallelism(64).Select(ip => (ip, Ping().Send(ip))))
{
    // Here 'i' is an object with the properties 'ip' and 'pingReply'
    Console.WriteLine($"Ping status: {i.pingReply.Status} for the target IP address: {i.ip}");
}

编辑: 现在才注意到 haim770 posted basically this in

编辑 2: 谢谢 pinkfloydx33 for pointing out I could use tuple deconsturcting

并行度=64并行调用同步Ping.Send效率很低,因为并行执行时阻塞了多达64个线程(前提是ThreadPool有足够的线程能否满足需求,这是值得怀疑的)。例如,执行 ping 的更有效方法是使用异步 Ping.SendPingAsync method. To invoke this method in a way that no more than 64 asynchronous operations will be simultaneously in-flight, you will need a Parallel.ForEach equivalent that works with asynchronous delegates. Currently there is no such thing available built-in (it will probably be available in .NET 6), but you can find lots of custom implementations if you search for ForEachAsync. There is one here。然后你就可以这样做了:

var ipList = new List<string>() {"192.168.0.1", "192.168.0.2", "192.168.0.3"}; // etc

ipList.ForEachAsync(async ip =>
{
    var ping = new Ping();
    var reply = await ping.SendPingAsync(ip);
    Console.WriteLine($"IP '{ip}' ping reply status: {reply.Status}");
}, dop: 64).Wait();

您也可以 await 完成操作,而不是使用阻塞 Wait,前提是您从 async 方法调用它。