[英]python asyncio add tasks dynamically
我正在 python 中学习 asyncio,我想尝试使用 asyncio。 我的目标是连续读取用户的输入并使用该输入来创建需要异步运行的作业/任务。
import asyncio
import os
loop = asyncio.get_event_loop()
async def action():
inp = int(input('enter: '))
await asyncio.sleep(inp)
os.system(f"say '{inp} seconds waited'")
async def main():
while True:
await asyncio.ensure_future(action())
try:
asyncio.run(main())
except Exception as e:
print(str(e))
finally:
loop.close()
我搞砸了一些事情,我想知道如何实现它。 每次用户输入一个数字,脚本需要休眠给定的时间,然后说出它已经等待。 整个事情需要同时进行。 如果用户输入 100 作为输入,脚本应该启动一个任务休眠 100 秒,但是在用户端,它需要在用户输入时再次请求输入。
您的代码的主要问题是您直接在异步 function 中调用了input()
。 input
本身是一个阻塞 function 并且在读取换行符或文件结尾之前不会返回。 这是一个问题,因为 Python 异步代码仍然是单线程的,如果存在阻塞 function,则不会执行其他任何操作。 在这种情况下,您需要使用run_in_executor 。
您的代码的另一个问题虽然与您的问题没有直接关系,但您混合了 pre-python3.7 调用事件循环的方式和 python3.7+ 方式。 根据文档, asyncio.run
使用。 如果要使用 pre 3.7 调用循环的方式,正确的方式是
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
或者
loop = asyncio.get_event_loop()
asyncio.ensure_future(main())
loop.run_forever()
由于您的main()
中有一段while True
,因此run_until_complete
和run_forever
之间没有区别。
最后,在main()
() 中使用ensure_future()
毫无意义。 ensure_future
的重点是提供“正常”(即非异步)function 一种将事物安排到事件循环中的方法,因为它们不能使用await
关键字。 使用ensure_future
的另一个原因是,如果您想安排许多具有高 io-bounds 的任务(例如网络请求)而不等待它们的结果。 由于您正在await
调用 function ,因此自然没有使用ensure_future
的意义。
这是修改后的版本:
import asyncio
import os
async def action():
loop = asyncio.get_running_loop()
inp = await loop.run_in_executor(None, input, 'Enter a number: ')
await asyncio.sleep(int(inp))
os.system(f"say '{inp} seconds waited'")
async def main():
while True:
await action()
asyncio.run(main())
在这个版本中,在输入用户输入之前,代码执行在await action()
和await loop.run_in_executor()
之间交替执行。 当没有安排其他任务时,事件循环大部分是空闲的。 但是,当有事情被安排时(使用await sleep()
模拟),那么控制权自然会转移到被安排的长时间运行的任务上。
Python 异步编程的一个关键是您必须确保偶尔将控制权转移回事件循环,以便可以运行其他计划的事情。 每当遇到await
时都会发生这种情况。 在您的原始代码中,解释器卡在input()
并且永远没有机会将 go 返回事件循环,这就是为什么在提供用户输入之前不会执行其他计划任务的原因。
你可以尝试这样的事情:
import asyncio
WORKERS = 10
async def worker(q):
while True:
t = await q.get()
await asyncio.sleep(t)
q.task_done()
print(f"say '{t} seconds waited'")
async def main():
q = asyncio.Queue()
tasks = []
for _ in range(WORKERS):
tasks.append(asyncio.create_task(worker(q)))
print(f'Keep inserting numbers, "q" to quit...')
while (number := await asyncio.to_thread(input)) != "q":
q.put_nowait(int(number))
await q.join()
for task in tasks:
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)
if __name__ == "__main__":
asyncio.run(main())
测试:
$ python test.py
Keep inserting numbers, "q" to quit...
1
say '1 seconds waited'
3
2
1
say '1 seconds waited'
say '2 seconds waited'
say '3 seconds waited'
q
注意:由于使用了一些新语法 ( :=
) 和 function ( asyncio.to_thread
),因此需要Python 3.9+。
import asyncio
async def aworker(q):
''' Worker that takes numbers from the queue and prints them '''
while True:
t = await q.get() # Wait for a number to be put in the queue
print(f"{t} received {asyncio.current_task().get_coro().__name__}:{asyncio.current_task().get_name()}")
await asyncio.sleep(t)
q.task_done()
print(f"waited for {t} seconds in {asyncio.current_task().get_coro().__name__}:{asyncio.current_task().get_name()}")
async def looper():
''' Infinite loop that prints the current task name '''
i = 0
while True:
i+=1
await asyncio.sleep(1)
print(f"{i} {asyncio.current_task().get_name()}")
names = []
for task in asyncio.all_tasks():
names.append(task.get_name())
print(names)
async def main():
q = asyncio.Queue()
tasks = []
# create two worker tasks and one infinitely looping task
tasks.append(asyncio.create_task(aworker(q), name="aworker 1")) # Create a worker which handles input from the queue
tasks.append(asyncio.create_task(aworker(q), name="aworker 2")) # Create another worker which handles input from the queue
tasks.append(asyncio.create_task(looper(),name="looper")) # Create a looper task which prints the current task name and the other running tasks
for task in tasks:
# print the task names thus far
print(task.get_name())
print(f'Keep inserting numbers, "q" to quit...')
''' asyncio.thread names itself Task-1 '''
while (number := await asyncio.to_thread(input)) != "q":
try:
q.put_nowait(int(number))
except ValueError:
print("Invalid number")
await q.join()
for task in tasks:
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)
if __name__ == "__main__":
asyncio.run(main())
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.