我已经使用此代码来测试asyncio的工作原理:
stop = False
async def subcoro():
# same result even with a range of 30
for i in range(30000):
pass
async def first():
global stop
while not stop:
await subcoro()
# without sleep no signal is triggered
await asyncio.sleep(0.1)
async def main(loop):
coro = asyncio.ensure_future(first())
await asyncio.wait([coro], loop=loop)
def end():
global stop
stop = True
if __name__ == '__main__':
loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
loop.add_signal_handler(getattr(signal, signame), end)
try:
loop.run_until_complete(asyncio.ensure_future(main(loop)))
finally:
loop.close()
print('Bye!')
为什么我要让first()
睡一会儿(甚至小于0.1)以使程序处理信号?
还有另一种方法可以优雅地关闭所有协同程序和事件循环吗?
更新: 在python-forum.org上,他们告诉我在PEP-0492阅读,但它没有提供任何解决方案或建议。
UPDATE2: 我的真实应用:https://github.com/FedericoTorsello/Embedded/tree/serialIO
答案 0 :(得分:0)
我喜欢这个问题,我尝试的第一件事就是strace
两者:
使用sleep
我看到很多epoll
(等待信号,通过套接字“转发”,通常是为了符合接收信号处理所需的原子性)
如果没有sleep
没有epoll
,那么进程会收到信号,信息将被推送到sochet,但从未读过。
为什么?从语义的角度来看,:
while not stop:
await subcoro()
意义上的是“牢不可破的”
await
与yield from
类似,暂停[该]协程的执行,直到[等待]等待完成并返回结果数据。
但是当你的subcoro
没有回到循环中时,它会立即返回,所以“await”被满足并且循环再次循环,永远不会给主循环带来机会赶上来。
所以你真的处在一个“无限循环”中,永远不会把手交给主循环。
现在,使用asyncio.sleep
,您将手放回主循环,显然,asyncio.sleep
的实现会这样做,因此主循环可以在睡眠持续时间内执行其他操作就像检查网络事件一样,比如在套接字上收到的信号。还有另一种方法可以明确地将手放回循环,是一个空的“收益”,如:
@asyncio.coroutine
def cooperate():
yield
return
现在调用await cooperate()
与await asyncio.sleep(0.1)
实际上没有实际睡眠的效果相同,这是asyncio.sleep
在延迟0
时所做的事情:
@coroutine
def sleep(delay, result=None, *, loop=None):
"""Coroutine that completes after a given time (in seconds)."""
if delay == 0:
yield
return result
[...]
从实施的角度来看,现在:
在休眠状态下,asyncio.base_events.BaseEventLoop._run_once
会被重复调用,但是如果没有sleep
则永远不会返回,可能是因为你的first
永远不会返回它,我没有深入检查它。
总结:
subcoro
没有任何意义,在现实世界的应用程序中,它会给主循环一些时间,通常是通过调用网络或等待任何东西。
答案 1 :(得分:0)
由于loop.add ..信号(sig)可以在信号处理过程中与循环交互(无需显式传递它或其他类似doc的args)。在lib aiohttp / gunicorn之后,例如,在所有Coros except CustmException:
子句中引发CustomException()并进行清理。示例:
import asyncio # etc
class GracefulExit(SystemExit):
def __init__(self, msg=None, code=None):
super(GracefulExit, self).__init__(msg)
self.code = code
def handle_SIGTERM_exit(signum):
logging.debug(f'handle {signal.Signals(signum)!r}')
raise GracefulExit(code=0) # https://docs.python.org/3/library/asyncio-eventloop.html#set-signal-handlers-for-sigint-and-sigterm
def handle_SIGABRT_exit(signum): # else Fatal Python error: Aborted
raise GracefulExit(code=1)
async def main():
loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGTERM, functools.partial(handle_SIGTERM_exit, signal.SIGTERM))
loop.add_signal_handler(signal.SIGABRT, handle_SIGABRT_exit, signal.SIGABRT, None)
# add other coros to loop (that catch or finally:)
asyncio.run(main())