Доступ к значению, используемому в LINQ Select в 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 в этом контексте. Я действительно хотел бы понять, как я могу получить доступ к каждому родственнику ip, когда я их отправляю?

Я изучил PingReply объект, но PingReply.Address в качестве примера содержит IP-адрес хоста (отправителя), поэтому он не может помочь с этим требованием. Я бы очень хотел, чтобы объект PingReply содержал пропингованный 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}");
}

person BernardV    schedule 16.02.2021    source источник
comment
Просто используйте .Select(ip => new { ip, pingResult = new Ping().Send(ip) })   -  person haim770    schedule 16.02.2021
comment
Вместо этого используйте Parallel.ForEach(ipList, ip => { /*code to do ping and write result*/ });. читать будет легче.   -  person Llama    schedule 16.02.2021
comment
Спасибо @haim770! Ваш ответ имеет смысл и работает.   -  person BernardV    schedule 16.02.2021


Ответы (2)


В настоящее время вы выбираете только pingReply, а не ip и pingReply, для этого вам нужно выбрать новый анонимный тип и перебрать его. Вот так:

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 опубликовал в основном это в их комментарий

Редактировать 2: Спасибо pinkfloydx33 за указание, что я могу использовать деконструкция кортежа

person MindSwipe    schedule 16.02.2021
comment
Спасибо за пример @MindSwipe! Имеет смысл. (P.S. просто перепроверьте свой пример, там была опечатка с отсутствием закрытия } - person BernardV; 16.02.2021
comment
Ах, спасибо @BernardV, не заметил пропущенной закрывающей скобки, так как сейчас сижу в школе. Я отредактировал ответ. Также не забудьте принять мой ответ, если он отвечает на ваш вопрос, чтобы будущие читатели знали, что это сработало (комментарии имеют тенденцию не задерживаться) - person MindSwipe; 16.02.2021
comment
Если вы выберете ValueTuple вместо этого, вы можете деструктурировать его в foreach... Это может быть яснее foreach (var (ip, reply) in ipList.XXX.Select(ip => (ip, new Ping().Send(ip)))) { Console.WriteLine($"status {reply.Status} - ip {ip}“); } - person pinkfloydx33; 16.02.2021
comment
Ах да, спасибо @pinkfloydx33, технически я это знаю, но мой мозг отключился. Я отредактирую свой вопрос, как только доберусь до компьютера - person MindSwipe; 17.02.2021

Вызов синхронного Ping.Send параллельно со степенью параллелизма = 64 весьма неэффективен, поскольку при параллельном выполнении блокируется аж 64 потока (при условии, что у ThreadPool достаточно потоков, чтобы удовлетворить спрос, что сомнительно). Более эффективный способ выполнить эхо-запрос — использовать асинхронный Ping.SendPingAsync. Чтобы вызвать этот метод таким образом, чтобы одновременно выполнялось не более 64 асинхронных операций, вам потребуется эквивалент Parallel.ForEach, который работает с асинхронными делегатами. В настоящее время такой встроенной функции нет (вероятно, она будет доступно в .NET 6), но вы можете найти множество пользовательских реализаций, если будете искать ForEachAsync. Например, здесь есть один здесь. Тогда вы сможете сделать это:

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.

person Theodor Zoulias    schedule 18.02.2021
comment
Спасибо, @Theodor Zoulias. Я изучу вашу рекомендацию; прежде чем остановиться на методе AsParallel().WithDegreeOfParallelism, я исследовал метод асинхронного пинга на примере: stackoverflow.com/a/22078703/3324415 по какой-то причине не чувствовал себя быстрее, чтобы выполнить всю задачу (я тоскую о 254 ip). - person BernardV; 19.02.2021
comment
если вам вообще интересно посмотреть на маленькое консольное приложение, оно у меня есть здесь github.com /BVisagie/Live-Host-Sweeper; ценю ваш отзыв на мой вопрос спасибо! - person BernardV; 19.02.2021
comment
@BernardV Я не могу объяснить, почему это не быстрее. Может быть какой-то другой ограничивающий фактор, например какое-то внутреннее максимальное количество одновременных пингов на домен. Я не знаю, просто предполагаю. - person Theodor Zoulias; 19.02.2021
comment
Привет, @Theodor Zoulias. Я реализовал ваше предложение, и оно кажется очень быстрым. Мне было интересно, можно ли что-нибудь сделать из-за характера упорядочения результатов, я полагаю, не из-за того, что я не контролирую выполнение задачи? У меня есть онлайн-пример: dotnetfiddle.net/jZrrjy - person BernardV; 19.02.2021
comment
@BernardV Я думаю, вы хотите получить результаты PingReply в том же порядке, что и IP-адреса в исходном списке. В этом случае вам, вероятно, понадобится версия ForEachAsync, предлагающая аналогичную функциональность с конфигурацией .AsParallel().AsOrdered() PLINQ. Жаль, что встроенные параллельные библиотеки пока не поддерживают асинхронность, и приходится использовать собственные решения или писать свои. Вы можете найти такую ​​реализацию здесь< /а>. - person Theodor Zoulias; 19.02.2021