3

Given a list of IP addresses:

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

I am attempting to loop over each IP in the list, in a parallel fashion and then print a meaningful message to screen:

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

I am unable to access ip in that context. I would really like to understand how I could go about accessing each relative ip as I am sending them out?

I have explored the PingReply object but PingReply.Address as an example contains the host (sender) IP, so it cannot help with this requirement. I really wish the PingReply object contained the Ip that was pinged!


UPDATE

As per example provided by @haim770 and @MindSwipe I ended up using:

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

UPDATE 2

As per comment from @pinkfloydx33 regarding use of ValueTuple I have done as per the following example:

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}");
}
BernardV
  • 640
  • 10
  • 28

2 Answers2

2

You're currently only selecting the pingReply, not the ip and the pingReply, to do that you'll need to select a new anonymous type and iterate over that. Like so:

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

Edit: Just noticed now that haim770 posted basically this in their comment

Edit 2: Thanks pinkfloydx33 for pointing out I could use tuple deconsturcting

MindSwipe
  • 7,193
  • 24
  • 47
  • 1
    Thanks for the example @MindSwipe! Makes sense. (P.S. just double check your example there was a typo with lack of closing `}` – BernardV Feb 16 '21 at 08:19
  • 1
    Ah thanks @BernardV, didn't notice the missing closing brace as I'm sitting in School right now. I edited the answer. Also make sure to accept my answer if it answers your question, so future readers know it worked out (comments have a tendency to not stick around) – MindSwipe Feb 16 '21 at 08:24
  • 1
    If you select a `ValueTuple` instead you can destructure it in the `foreach`... It might be clearer `foreach (var (ip, reply) in ipList.XXX.Select(ip => (ip, new Ping().Send(ip)))) { Console.WriteLine($"status {reply.Status} - ip {ip}“); }` – pinkfloydx33 Feb 16 '21 at 09:50
  • Ah yeah thanks @pinkfloydx33, I technically know that, but my brain blanked. I'll edit my question once I get to a computer – MindSwipe Feb 17 '21 at 10:24
0

Calling the synchronous Ping.Send in parallel with a degree of parallelism = 64 is quite inefficient, because as many as 64 threads are blocked during the parallel execution (provided that the ThreadPool has enough threads available to satisfy the demand, which is doubtful). A more efficient way to do the pinging is to use the asynchronous 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 for example. Then you will be able to do this:

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

You could also await the completion of the operation, instead of using the blocking Wait, provided that you are calling it from an async method.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • Thank you @Theodor Zoulias I will explore your recommendation; before settling on `AsParallel().WithDegreeOfParallelism` method I did explore the async ping method through example like: https://stackoverflow.com/a/22078703/3324415 but it for some reason did not feel any faster to complete the whole task (I am pining about 254 ip's). – BernardV Feb 19 '21 at 06:04
  • if you are at all interested in looking at the little console app, I have it up here https://github.com/BVisagie/Live-Host-Sweeper; appreciate your feedback on my question thanks! – BernardV Feb 19 '21 at 06:09
  • @BernardV I can't explain why it's not faster. There may be some other limiting factor, like some internal maximum number of simultaneous pings per domain. I don't know, just guessing. – Theodor Zoulias Feb 19 '21 at 06:12
  • Hi @Theodor Zoulias I have implemented your suggestion and it does seem to be very fast, I was wondering if anything could be done due to the nature of ordering of the results, I am assuming not due to not being in control of task completion? I have an online example here: https://dotnetfiddle.net/jZrrjy – BernardV Feb 19 '21 at 07:35
  • @BernardV I guess you want to get the `PingReply` results in the same order with the IPs in the source list. In that case you probably need a version of `ForEachAsync` that offers a similar functionality with the `.AsParallel().AsOrdered()` PLINQ configuration. It's a pity that the built-in parallel libraries are not async-enabled yet, and we must use custom solutions, or write our own. You can find such an implementation [here](https://stackoverflow.com/questions/10806951/how-to-limit-the-amount-of-concurrent-async-i-o-operations/64455549#64455549). – Theodor Zoulias Feb 19 '21 at 08:38