Ant*_*ton 10 python python-asyncio
如果我从扫描仪中删除这一行,
await asyncio.sleep(0)
Run Code Online (Sandbox Code Playgroud)
工作时间从5秒增长到400秒。
为什么会发生这种情况?
我的代码:
import os
import asyncio
import time
async def rl(response):
await asyncio.sleep(0)
return response.readlines()
async def scan_Ip(addr):
print(addr)
response = os.popen("ping -n 1 " + addr)
data = await rl(response)
for line in data:
if 'TTL' in line:
print(data)
async def scan():
tasks=[]
for ip in range(0, 256):
tasks.append(asyncio.create_task(scan_Ip(f'192.168.8.{ip}')))
await asyncio.wait(tasks)
if __name__ == '__main__':
start_time = time.time()
asyncio.run(scan())
print(f"--- {time.time() - start_time} seconds ---")
Run Code Online (Sandbox Code Playgroud)
use*_*342 20
正如 Nullman 在对该问题的评论中指出的那样,os.popen它不提供异步接口,因此从 asyncio 使用它会使协程阻塞事件循环。其结果是,无论是否使用gather或 ,多个此类协程都会按顺序执行wait。
请注意您的函数如何rl通过阻塞readlines()调用读取数据,并且实际上不等待任何内容(除了sleep(0))。这是一个可靠的指标,rl除了名称之外,它不是异步的,并且一旦您尝试并行运行它,它就会导致瓶颈。
与 asyncio 交互的正确方法ping是使用 asyncio 等效项os.popen:
async def scan_Ip(addr):
print(addr)
proc = await asyncio.create_subprocess_exec(
"ping", "-n", "1", addr, stdout=subprocess.PIPE
)
async for line in proc.stdout:
if 'TTL' in line:
print(data)
Run Code Online (Sandbox Code Playgroud)
现在,进程交互是通过异步调用执行的,这允许对所有 256 个进程并行执行它们。
至于为什么await asyncio.sleep(0)有帮助 - 等待asyncio.sleep()强制异步函数将控制权交给事件循环(即使延迟为 0)。这允许另一个协程执行到后续的await,依此类推,并最终以有利于该程序的方式影响执行顺序。如果没有asyncio.sleep(0)/scan_Ip对rl,则永远不会落入事件循环并按顺序执行,如下所示(简化):
1. x1 = os.popen("ping -n 1 192.168.8.0")
2. for line in x1.readlines(): ...
3. x2 = os.popen("ping -n 1 192.168.8.1")
4. for line in x2.readlines(): ...
...
511. x256 = os.popen("ping -n 1 192.168.8.255")
512. for line in x256.readlines(): ...
Run Code Online (Sandbox Code Playgroud)
在考虑另一个之前,每个ping都已启动、完全处理和停止。使用await asyncio.sleep(0)betweenos.popen()和readlines(),代码还不是完全异步的,但它确实在两个步骤之间添加了一个切换,导致执行顺序如下:
1. x1 = os.popen("ping -n 1 192.168.8.0")
2. x2 = os.popen("ping -n 1 192.168.8.1")
...
256. x256 = os.popen("ping -n 1 192.168.8.255")
257. for line in x1.readlines(): ...
258. for line in x2.readlines(): ...
...
512. for line in x256.readlines(): ...
Run Code Online (Sandbox Code Playgroud)
换句话说,执行相同的步骤,但重新排序,以便首先启动所有命令,然后才读取它们。这允许ping命令并行地产生输出(并连接到网络等)。然后,通信步骤仅拾取管道缓冲区中基本准备好的输出。所有并行执行的事实ping使得该版本速度更快。
最后,请注意,惯用的 asyncio 代码(如使用 的代码asyncio.subprocess)不需要asyncio.sleep(0),因为它在等待数据时已经一致地等待,并且强制额外的睡眠实际上会使其变慢。
小智 2
运行代码后,实际上它根本不会增加我机器上的运行时间(或仅增加最少的时间)。我认为这会以某种方式触发类似于速率限制(非常短的速率限制)的东西,并且有时必须重试,从而有效地使运行时间加倍。你的机器可能会有所不同,但这就是我在 MacOS Catalina 上得到的。
与asyncio.sleep(0):1.9657979011535645秒
没有asyncio.sleep(0): 3.2927019596099854 秒
| 归档时间: |
|
| 查看次数: |
8721 次 |
| 最近记录: |