我正在开发一个使用 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 >=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)))
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的答案基本相同,但有两点不同:
asyncio.create_task
,它比 asyncio.ensure_future
“更具可读性”。signame
绑定到 lambda 函数可以避免 后期绑定 导致 @R2RT 的 评论中提到的预期-意外™ 行为的问题。这是无耻地从Lynn Root 的博客文章中复制的:Graceful Shutdowns with asyncio(阅读整个系列以了解有关 asyncio 美丽的血腥的更多信息)。loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
loop.add_signal_handler(getattr(signal, signame),
asyncio.async, ask_exit(signame))
这样,信号会导致您的ask_exit 被安排在任务中。
python3.8
async def handler_shutdown
,并在传递到loop.create_task()
时将其包裹在
add_signal_handler()
def 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()中解决了这个问题