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

Jin*_*nog 14 python signals coroutine python-asyncio

我正在开发一个使用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()
Run Code Online (Sandbox Code Playgroud)

如果您运行此示例然后按Ctrl + C,则不会发生任何事情.问题是,如何使用siganls和coroutines来实现这种行为?

pog*_*otz 15

python >=3.7 的语法

\n
import asyncio\nimport signal\n\nloop = asyncio.get_event_loop()\n\n\nasync def ask_exit(signame):\n    print("got signal %s: exit" % signame)\n    await asyncio.sleep(10.0)  # simulate some async clean-up\n    loop.stop()\n\nfor signame in (\'SIGINT\', \'SIGTERM\'):\n    loop.add_signal_handler(getattr(signal, signame),\n                            lambda signame=signame: asyncio.create_task(ask_exit(signame)))\n\n# ...do stuff, create more tasks, etc...\n\nloop.run_forever()\n
Run Code Online (Sandbox Code Playgroud)\n

笔记

\n

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

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


svs*_*svs 11

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)))
Run Code Online (Sandbox Code Playgroud)

  • 这不会按预期工作。两个 lambda 表达式都通过引用捕获 `signame` 循环变量,并且两个 lambda 表达式都将被调用,就像发送了 `SIGTERM` 一样(调用了 `ask_exit('SIGTERM')`)。为每条信号线写入:`loop.add_signal_handler(signal.SIGINT, lambda: asyncio.ensure_future(ask_exit('SIGINT')))`是最简单的解决方案 (2认同)

小智 5

loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
    loop.add_signal_handler(getattr(signal, signame),
                            asyncio.async, ask_exit(signame))
Run Code Online (Sandbox Code Playgroud)

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

  • 这是python 3.5.1中的无效语法。 (2认同)