24

我正在开发一个使用 python3.4 中的 asyncio 进行网络的应用程序。当此应用程序完全关闭时,节点需要与集线器“断开连接”。此断开连接是一个需要网络连接的活动过程,因此循环需要等待此过程完成才能关闭。

我的问题是使用协程作为信号处理程序将导致应用程序不关闭。请考虑以下示例:

import asyncio
import functools
import os
import signal

@asyncio.coroutine
def ask_exit(signame):
    print("got signal %s: exit" % signame)
    yield from asyncio.sleep(10.0)
    loop.stop()

loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
    loop.add_signal_handler(getattr(signal, signame),
                                        functools.partial(ask_exit, signame))

print("Event loop running forever, press CTRL+c to interrupt.")
print("pid %s: send SIGINT or SIGTERM to exit." % os.getpid())
loop.run_forever()

如果您运行此示例,然后按 Ctrl+C,则不会发生任何事情。问题是,如何让信号和协程发生这种行为?

4

4 回答 4

17

python的语法> = 3.5

loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
    loop.add_signal_handler(getattr(signal, signame),
                            lambda: asyncio.ensure_future(ask_exit(signame)))
于 2017-02-10T04:59:51.717 回答
5
loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
    loop.add_signal_handler(getattr(signal, signame),
                            asyncio.async, ask_exit(signame))

这样,信号会导致您的 ask_exit 被安排在任务中。

于 2014-08-07T08:58:59.243 回答
3

python3.8

  • 第一次尝试:使用async def handler_shutdown,并在 loop.create_task()传递给时将其包裹起来add_signal_handler()
  • 第二次尝试:不要对def handler_shutdown().
  • 第三次尝试:包装 handler_shutdown 和参数functools.partial()

例如

import asyncio
import functools
def handler_shutdown(signal, loop, tasks, http_runner, ):
    ...
    ...
def main():
    loop = asyncio.get_event_loop()
    for signame in ('SIGINT', 'SIGTERM', 'SIGQUIT'):
                print(f"add signal handler {signame} ...")
                loop.add_signal_handler(
                    getattr(signal, signame),
                    functools.partial(handler_shutdown,
                            signal=signame, loop=loop, tasks=tasks,
                            http_runner=http_runner
                            )
                    )
  • 我遇到的主要问题是错误

    raise TypeError("coroutines cannot be used "

  • 通过将例程包装在 loop.create_task() 中来解决它

  • 然后通过删除异步表单信号处理函数来解决它
  • 对于处理程序的命名参数也使用 functools.partial
于 2020-02-15T08:30:26.367 回答
1

python的语法> = 3.7

loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
    loop.add_signal_handler(getattr(signal, signame),
                            lambda signame=signame: asyncio.create_task(ask_exit(signame)))

笔记

这与@svs 的答案基本相同,但有两个不同:

  1. 使用较新的 Python 3.7+ 方法asyncio.create_task,它比asyncio.ensure_future.
  2. 立即绑定signame到 lambda 函数可以避免后期绑定问题,从而导致@R2RT的评论中提到的预期意外™ 行为。这是从Lynn Root 的博客文章中无耻地复制的:Graceful Shutdowns with asyncio (阅读整个系列以了解更多关于 asyncio 的美丽血腥)。
于 2021-09-22T11:42:00.503 回答