python asyncio解释和信号处理程序

时间:2016-05-26 00:28:36

标签: python python-3.x async-await python-asyncio

我已经使用此代码来测试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

2 个答案:

答案 0 :(得分:0)

我喜欢这个问题,我尝试的第一件事就是strace两者:

使用sleep我看到很多epoll(等待信号,通过套接字“转发”,通常是为了符合接收信号处理所需的原子性)

如果没有sleep没有epoll,那么进程会收到信号,信息将被推送到sochet,但从未读过。

为什么?从语义的角度来看,:

while not stop:
    await subcoro()

意义上的

是“牢不可破的”

  

awaityield 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())