Python 为什么asyncio.sleep(0)使我的代码更快?
如果我从扫描仪中删除这行Python 为什么asyncio.sleep(0)使我的代码更快?,python,python-asyncio,Python,Python Asyncio,如果我从扫描仪中删除这行 await asyncio.sleep(0) 工作时间从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.po
await asyncio.sleep(0)
工作时间从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 ---")
在运行代码之后,它实际上根本不会增加我机器上的运行时间(或者只增加最少的次数)。我假设这会以某种方式触发类似于速率限制(非常短)的事件,并且有时必须重试,从而有效地将运行时间加倍。你的机器可能不同,但这是我在MacOS Catalina上得到的 使用
异步睡眠(0)
:1.9657979011535645秒
如果没有
asyncio.sleep(0)
:运行代码后3.2927019596099854秒,它实际上根本不会增加我的计算机上的运行时间(或者只增加最短时间)。我假设这会以某种方式触发类似于速率限制(非常短)的事件,并且有时必须重试,从而有效地将运行时间加倍。你的机器可能不同,但这是我在MacOS Catalina上得到的
使用异步睡眠(0)
:1.9657979011535645秒
如果没有
asyncio.sleep(0)
:3.2927019596099854秒正如Nullman在一篇关于这个问题的评论中指出的那样,os.popen
不提供异步接口,因此从asyncio使用它会使执行此操作的协程阻塞事件循环。这样做的结果是,无论使用gather
或wait
如何,多个这样的协同程序都会顺序执行
请注意,您的rl
函数如何通过阻塞readlines()
调用读取数据,而实际上并不等待任何操作(除了sleep(0)
)。这是一个可靠的指标,表明除名称外,rl
不是异步的,并且一旦您尝试并行运行它,它将导致瓶颈
从asyncio与ping
接口的正确方法是使用与os.popen
等效的asyncio:
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)
现在通过异步调用执行进程交互,这允许对所有256个进程并行执行它们
至于为什么等待asyncio.sleep(0)
帮助-等待asyncio.sleep()
强制异步函数将控制权交给事件循环()。这允许另一个协同程序执行后续的等待
,以此类推,并最终以对该程序有益的方式影响执行顺序。如果没有asyncio.sleep(0)
scan\u Ip/rl
对,则不会落到事件循环中,并按顺序执行,如下所示(简化):
换句话说,在考虑另一个之前,每个ping
都会启动、完全处理和停止。在os.popen()
和readlines()
之间使用wait asyncio.sleep(0)
,代码还不是完全异步的,但它确实在这两个步骤之间添加了一个开关,导致以下执行顺序:
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(): ...
换句话说,执行相同的步骤,但重新排序,以便首先启动所有命令,然后才从中读取命令。这允许ping
命令并行生成其输出(并连接到网络等)。然后,通信步骤只拾取基本就绪的输出,该输出位于管道缓冲区中。所有ping
s并行执行的事实使该版本更快
最后,请注意,惯用的异步IO代码,作为使用
asyncio.subprocess
的代码,不需要asyncio.sleep(0)
,因为它在等待数据时已经一直在等待,而强制额外的睡眠实际上会使它变慢。正如Nullman在对该问题的评论中指出的那样,os.popen
不提供异步接口,因此从asyncio使用它会使执行此操作的协程阻塞事件循环。这样做的结果是,无论使用gather
或wait
如何,多个这样的协同程序都会顺序执行
请注意,您的rl
函数如何通过阻塞readlines()
调用读取数据,而实际上并不等待任何操作(除了sleep(0)
)。这是一个可靠的指标,表明除名称外,rl
不是异步的,并且一旦您尝试并行运行它,它将导致瓶颈
从asyncio与ping
接口的正确方法是使用与os.popen
等效的asyncio:
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)
现在通过异步调用执行进程交互,这允许对所有256个进程并行执行它们
至于为什么等待asyncio.sleep(0)
帮助-等待asyncio.sleep()
强制异步函数将控制权交给事件循环()。这允许另一个协同程序执行后续的等待
,以此类推,并最终以对该程序有益的方式影响执行顺序。如果没有asyncio.sleep(0)
scan\u Ip/rl
对,则不会落到事件循环中,并按顺序执行,如下所示(简化):
换句话说,在考虑另一个之前,每个ping
都会启动、完全处理和停止。在os.popen()
和readlines()
之间使用wait asyncio.sleep(0)
,代码还不是完全异步的,但它确实在这两个步骤之间添加了一个开关,导致以下执行顺序:
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(): ...
换句话说,执行相同的步骤,但重新排序,以便首先启动所有命令,然后才从中读取命令。这允许ping
命令并行生成其输出(并连接到网络等)。然后,通信步骤只拾取基本就绪的输出,该输出位于管道缓冲区中。所有ping
s并行执行的事实使该版本更快
最后,请注意惯用的异步IO代码<