如何创建一个带有滚动协程 运行 的事件循环?
How to create an event loop with rolling coroutines running on it forever?
为了防止上下文切换,我想创建一个大循环来为网络连接和一些例程服务。
这是正常功能的实现:
import asyncio
import time
def hello_world(loop):
print('Hello World')
loop.call_later(1, hello_world, loop)
def good_evening(loop):
print('Good Evening')
loop.call_later(1, good_evening, loop)
print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)
try:
# Blocking call interrupted by loop.stop()
print('step: loop.run_forever()')
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print('step: loop.close()')
loop.close()
这是协程的实现:
import asyncio
@asyncio.coroutine
def hello_world():
while True:
yield from asyncio.sleep(1)
print('Hello World')
@asyncio.coroutine
def good_evening():
while True:
yield from asyncio.sleep(1)
print('Good Evening')
print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
print('step: loop.run_until_complete()')
loop.run_until_complete(asyncio.wait([
hello_world(),
good_evening()
]))
except KeyboardInterrupt:
pass
finally:
print('step: loop.close()')
loop.close()
和混合的:
import asyncio
import time
def hello_world(loop):
print('Hello World')
loop.call_later(1, hello_world, loop)
def good_evening(loop):
print('Good Evening')
loop.call_later(1, good_evening, loop)
@asyncio.coroutine
def hello_world_coroutine():
while True:
yield from asyncio.sleep(1)
print('Hello World Coroutine')
@asyncio.coroutine
def good_evening_coroutine():
while True:
yield from asyncio.sleep(1)
print('Good Evening Coroutine')
print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)
print('step: asyncio.async(hello_world_coroutine)')
asyncio.async(hello_world_coroutine())
print('step: asyncio.async(good_evening_coroutine)')
asyncio.async(good_evening_coroutine())
try:
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print('step: loop.close()')
loop.close()
如您所见,每个协程函数都有一个 while 循环。我怎样才能让它像普通的一样? IE。完成后,在给定的延迟时间后调用自身,但不只是在那里放一个循环。
你真的尝试 运行 你给出的三个例子吗?行为上的差异非常明显。
既然你从来没有说过你期望的话,就不知道什么是对的,什么不是。这三种实现可能是对的,也可能是错的。我可以告诉你每个实现有什么行为,以及为什么会有这样的行为;正确与否只有自己知道
第二个例子(yield from asyncio.sleep(1)
),两个协程是运行并发的。这意味着每个人都将自己执行; hello_world
每秒打印 Hello World
,good_evening
每秒打印 Good Evening
。
另外两个例子都使用了time.sleep(1)
,这是阻塞的。这意味着当第一个函数(取其为hello_world
)到达time.sleep(1)
时,整个程序将挂起一秒钟。这意味着当 hello_world
睡眠时,good_evening
也不能 运行,反之亦然。
程序是这样执行的:
- 进入循环。
- 循环调用
hello_world
.
已达到 hello_world
中的 time.sleep(1)
。程序休眠一秒。
Hello World
已打印。
hello_world
产量。
- 循环调用
good_evening
.
Good Evening
已打印。
time.sleep(1)
达到 good_evening
。程序休眠一秒。
good_evening
产量。
- 转到 2。
因此 Hello World
和 Good Evening
每 两 秒出现一次,因为每个 print
之间有两次 time.sleep(1)
调用.如果你 运行 这两个例子,你会很容易注意到。
import asyncio
@asyncio.coroutine
def hello_world_coroutine():
yield from asyncio.sleep(1)
print('Hello World Coroutine')
yield from hello_world_coroutine()
@asyncio.coroutine
def good_evening_coroutine():
yield from asyncio.sleep(1)
print('Good Evening Coroutine')
yield from good_evening_coroutine()
print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
print('step: loop.run_until_complete()')
loop.run_until_complete(asyncio.wait([
hello_world_coroutine(),
good_evening_coroutine()
]))
except KeyboardInterrupt:
pass
finally:
print('step: loop.close()')
loop.close()
UPD
此代码将达到最大递归深度。可能是因为 Python 没有尾调用优化。此处留下代码作为错误示例。
如果你真的想从协程中消除 while 循环(我不确定你为什么觉得有必要;这是做你想做的事情的最自然的方式),你可以使用 asyncio.async
(或 Python 3.4.4+ 上的 asyncio.ensure_future
)在下一次事件循环迭代中再次将协程安排到 运行:
import asyncio
@asyncio.coroutine
def hello_world():
yield from asyncio.sleep(1)
print('Hello World')
asyncio.async(hello_world())
@asyncio.coroutine
def good_evening():
yield from asyncio.sleep(1)
print('Good Evening')
asyncio.async(good_evening())
print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
print('step: loop.run_until_complete()')
asyncio.async(hello_world())
asyncio.async(good_evening())
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print('step: loop.close()')
loop.close()
请注意,如果您这样做,您必须切换回使用 loop.run_forever()
,因为 hello_world
/good_evening
将在现在打印后立即退出。
# asyncio_coroutine_forever.py
import asyncio
async def hello_world():
await asyncio.sleep(1)
print('Hello World')
await good_evening()
async def good_evening():
await asyncio.sleep(1)
print('Good Evening')
await hello_world()
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(hello_world())
loop.run_until_complete(good_evening())
loop.run_forever()
finally:
print('closing event loop')
loop.close()
为了防止上下文切换,我想创建一个大循环来为网络连接和一些例程服务。
这是正常功能的实现:
import asyncio
import time
def hello_world(loop):
print('Hello World')
loop.call_later(1, hello_world, loop)
def good_evening(loop):
print('Good Evening')
loop.call_later(1, good_evening, loop)
print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)
try:
# Blocking call interrupted by loop.stop()
print('step: loop.run_forever()')
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print('step: loop.close()')
loop.close()
这是协程的实现:
import asyncio
@asyncio.coroutine
def hello_world():
while True:
yield from asyncio.sleep(1)
print('Hello World')
@asyncio.coroutine
def good_evening():
while True:
yield from asyncio.sleep(1)
print('Good Evening')
print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
print('step: loop.run_until_complete()')
loop.run_until_complete(asyncio.wait([
hello_world(),
good_evening()
]))
except KeyboardInterrupt:
pass
finally:
print('step: loop.close()')
loop.close()
和混合的:
import asyncio
import time
def hello_world(loop):
print('Hello World')
loop.call_later(1, hello_world, loop)
def good_evening(loop):
print('Good Evening')
loop.call_later(1, good_evening, loop)
@asyncio.coroutine
def hello_world_coroutine():
while True:
yield from asyncio.sleep(1)
print('Hello World Coroutine')
@asyncio.coroutine
def good_evening_coroutine():
while True:
yield from asyncio.sleep(1)
print('Good Evening Coroutine')
print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
print('step: loop.call_soon(hello_world, loop)')
loop.call_soon(hello_world, loop)
print('step: loop.call_soon(good_evening, loop)')
loop.call_soon(good_evening, loop)
print('step: asyncio.async(hello_world_coroutine)')
asyncio.async(hello_world_coroutine())
print('step: asyncio.async(good_evening_coroutine)')
asyncio.async(good_evening_coroutine())
try:
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print('step: loop.close()')
loop.close()
如您所见,每个协程函数都有一个 while 循环。我怎样才能让它像普通的一样? IE。完成后,在给定的延迟时间后调用自身,但不只是在那里放一个循环。
你真的尝试 运行 你给出的三个例子吗?行为上的差异非常明显。
既然你从来没有说过你期望的话,就不知道什么是对的,什么不是。这三种实现可能是对的,也可能是错的。我可以告诉你每个实现有什么行为,以及为什么会有这样的行为;正确与否只有自己知道
第二个例子(yield from asyncio.sleep(1)
),两个协程是运行并发的。这意味着每个人都将自己执行; hello_world
每秒打印 Hello World
,good_evening
每秒打印 Good Evening
。
另外两个例子都使用了time.sleep(1)
,这是阻塞的。这意味着当第一个函数(取其为hello_world
)到达time.sleep(1)
时,整个程序将挂起一秒钟。这意味着当 hello_world
睡眠时,good_evening
也不能 运行,反之亦然。
程序是这样执行的:
- 进入循环。
- 循环调用
hello_world
.
已达到 time.sleep(1)
。程序休眠一秒。Hello World
已打印。hello_world
产量。- 循环调用
good_evening
. Good Evening
已打印。time.sleep(1)
达到good_evening
。程序休眠一秒。good_evening
产量。- 转到 2。
hello_world
中的 因此 Hello World
和 Good Evening
每 两 秒出现一次,因为每个 print
之间有两次 time.sleep(1)
调用.如果你 运行 这两个例子,你会很容易注意到。
import asyncio
@asyncio.coroutine
def hello_world_coroutine():
yield from asyncio.sleep(1)
print('Hello World Coroutine')
yield from hello_world_coroutine()
@asyncio.coroutine
def good_evening_coroutine():
yield from asyncio.sleep(1)
print('Good Evening Coroutine')
yield from good_evening_coroutine()
print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
print('step: loop.run_until_complete()')
loop.run_until_complete(asyncio.wait([
hello_world_coroutine(),
good_evening_coroutine()
]))
except KeyboardInterrupt:
pass
finally:
print('step: loop.close()')
loop.close()
UPD
此代码将达到最大递归深度。可能是因为 Python 没有尾调用优化。此处留下代码作为错误示例。
如果你真的想从协程中消除 while 循环(我不确定你为什么觉得有必要;这是做你想做的事情的最自然的方式),你可以使用 asyncio.async
(或 Python 3.4.4+ 上的 asyncio.ensure_future
)在下一次事件循环迭代中再次将协程安排到 运行:
import asyncio
@asyncio.coroutine
def hello_world():
yield from asyncio.sleep(1)
print('Hello World')
asyncio.async(hello_world())
@asyncio.coroutine
def good_evening():
yield from asyncio.sleep(1)
print('Good Evening')
asyncio.async(good_evening())
print('step: asyncio.get_event_loop()')
loop = asyncio.get_event_loop()
try:
print('step: loop.run_until_complete()')
asyncio.async(hello_world())
asyncio.async(good_evening())
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print('step: loop.close()')
loop.close()
请注意,如果您这样做,您必须切换回使用 loop.run_forever()
,因为 hello_world
/good_evening
将在现在打印后立即退出。
# asyncio_coroutine_forever.py
import asyncio
async def hello_world():
await asyncio.sleep(1)
print('Hello World')
await good_evening()
async def good_evening():
await asyncio.sleep(1)
print('Good Evening')
await hello_world()
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(hello_world())
loop.run_until_complete(good_evening())
loop.run_forever()
finally:
print('closing event loop')
loop.close()