asyncio - 如何在信号处理程序中使用协程?

问题描述 投票:0回答:4

我正在开发一个使用 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,则不会发生任何事情。 问题是,如何通过信号和协程来实现这种行为?

python signals coroutine python-asyncio
4个回答
20
投票

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

12
投票

python >=3.7 的语法

import asyncio
import signal

loop = asyncio.get_event_loop()


async def ask_exit(signame):
    print("got signal %s: exit" % signame)
    await asyncio.sleep(10.0)  # simulate some async clean-up
    loop.stop()

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

# ...do stuff, create more tasks, etc...

loop.run_forever()

注意

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

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

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 被安排在任务中。


4
投票

python3.8

  • 第一次尝试:使用
    async def handler_shutdown
    ,并在传递到
    loop.create_task()
    时将其包裹在
    add_signal_handler()
  • 第二次尝试:不要使用异步
    def handler_shutdown()
  • 第三次尝试:将 handler_shutdown 和 param 包装在
    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
© www.soinside.com 2019 - 2024. All rights reserved.