如何使用多线程ping一系列IP地址

5
假设我有以下 IP 范围 "10.0.0.x"。我需要循环这些 IP 范围内的所有 IP 地址 - "10.0.0.1-255",对每个 IP 地址进行 ping 测试,并检查响应。
以下是我的代码:
for ip in range(1, 256):
  fullIP = '10.0.0' + ip
  if(Ping(fullIP) == True):
    print(fullIP + ' responded')
  else:
    print(fullIP + ' did not respond')

这段代码是可行的,但很慢。
我想通过多线程使其更加高效,所以我做了以下操作:

def PingRange(start, end):
  for ip in range(start, end):
  fullIP = '10.0.0' + ip
  if(Ping(fullIP) == True):
    print(fullIP + ' responded')
  else:
    print(fullIP + ' did not respond')

try:
  thread.start_new_thread( PingRange, (1, 123))
  thread.start_new_thread( PingRange, (123, 256))
except:
  print "Error: unable to start thread"

这段代码虽然可用,但可以更好地实现更广泛的应用。
如果这段代码被正确编写,那么我不会一直创建两个线程;而是会创建尽可能多的线程,取决于操作系统允许的最大数量。
有些计算机允许8个线程,有些只允许4个,甚至有些不允许线程。

如何在Python中使此程序使用最大数量的线程?


1
有些计算机允许8个线程,有些只允许4个,还有一些甚至不允许线程。那么我们在谈论什么样的架构呢? - Cthulhu
为什么架构很重要?我希望代码在所有架构上都能尽可能高效地运行。 - yuval
因为现实中没有任何系统无法处理仅有的256个线程。 - Cthulhu
我建议在这里添加更多细节,首先你的“Ping”函数采用什么形式?因为据我所知它不是内置函数。其次,你是否考虑过使用多进程技术中的池函数(pool function)?由于Python使用全局解释器锁,一个进程中只能运行一个线程,这意味着通常使用多个线程实际上会减慢任务的速度(而不是加快)。如果要同时执行多个任务,你需要使用多个进程。 - James Kent
1个回答

5
这个问题适合使用线程池。线程池使用恒定数量的线程运行工作项(函数或方法),并在其线程池中执行这些工作项。它具有内置排队功能,因此如果将一百个工作项提供给五个线程的线程池,则会执行所有一百个项,但不会同时运行超过五个。

Python中有两个内置的线程池选项(取决于您使用的版本) - multiprocessing.dummy.Poolconcurrent.futures.ThreadPoolExecutorThreadPoolExecutor仅内置于Python 3.x中,但可以从PyPI获得后移植。 multiprocessing.dummy.Pool在2.6+中可用。使用multiprocessing.dummy.Pool,您的代码变得简单如下:

import multiprocessing.dummy

def ping_range(start, end):
    num_threads = # Number of threads to run in the pool.
    p = multiprocessing.dummy.Pool(num_threads)
    p.map(ping, [10.0.0.x for x in range(start,end)])

if __name__ == "__main__":
    PingRange(0, 255)

下一个问题是应该使用什么来设置“num_threads”。我认为你对系统具有最大允许线程数量的了解略微有误。在任何系统上,您都可以创建任意数量的“Thread”对象,并且没有任何实际限制,但是在某个点上,您将创建如此多的线程,以至于系统无法处理它,性能会开始变得更糟糕,而不是更好。
对于CPU密集型应用程序(即主要需要CPU执行工作的应用程序),经验法则是运行与CPU数量相同的线程。但是,这个“ping”操作是I/O密集型的,这意味着大部分工作是向外部系统发送ping请求,然后等待响应,这不需要CPU做任何事情。在这种情况下,通常可以使用比CPU数量更多的线程。我们可以保守地使用“2 * number_of_cpus”,但您也可以尝试使用更大的数字。
import multiprocessing
num_threads = 2 * multiprocessing.cpu_count()

将所有东西放在一起:
import multiprocessing.dummy
import multiprocessing

def ping(ip):
   success = # Run the ping command here
   if success:
       print("{} responded".format(ip))
   else:
       print("{} did not respond".format(ip))
   return success

def ping_range(start, end):
    num_threads = 2 * multiprocessing.cpu_count()
    p = multiprocessing.dummy.Pool(num_threads)
    p.map(ping, [10.0.0.x for x in range(start,end)])

if __name__ == "__main__":
    ping_range(0, 255)

谢谢,这段代码真的很简单并且有效,但如果它能有异常处理就更好了。有时候当我在其他电脑上运行这段代码时,会抛出异常。你能给这段代码加上异常处理吗? - yuval

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接