在 Signal Handler、Asyncio 中调用 Async 函数
Call Async function in Signal Handler, Asyncio
我正在使用 discord.py 制作 Discord 机器人,有时我用来 运行 的脚本需要关闭机器人。当我在不使用信号处理程序的情况下关闭它时,有很多关于循环未关闭的错误,所以我添加了一个信号处理程序(使用下面的代码)并且在内部我需要调用 client.close()
和 client.logout()
,但问题是那些是异步函数,因此需要我等待它们,但我不能等待这些函数,因为信号处理程序不能是异步函数。
代码如下:
def handler():
print("Logging out of Discord Bot")
client.logout()
client.close()
sys.exit()
@client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
for signame in ('SIGINT', 'SIGTERM'):
client.loop.add_signal_handler(getattr(signal, signame),
lambda: asyncio.ensure_future(handler()))
有没有办法使用信号处理程序正确注销,或者至少只是让代码内部的警告和错误静音,这样就不会在控制台中打印错误。
您的方法是正确的 - 因为 add_signal_handler
需要一个普通函数而不是异步函数,您确实需要调用 ensure_future
(或其堂兄弟 create_task
)来在事件循环中向 运行 提交一个异步函数。下一步是实际使 handler
异步,并且 await
它调用的协程:
async def handler():
print("Logging out of Discord Bot")
await client.logout()
await client.close()
asyncio.get_event_loop().stop()
请注意,我将 sys.exit()
更改为显式停止事件循环,因为 asyncio 对从回调中间调用的 sys.exit()
反应不佳(它捕获 SystemExit
异常并抱怨未检索的异常)。
由于我没有discord可以测试,所以我通过更改注销和睡眠关闭来测试它:
import asyncio, signal
async def handler():
print("sleeping a bit...")
await asyncio.sleep(0.2)
print('exiting')
asyncio.get_event_loop().stop()
def setup():
loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
loop.add_signal_handler(getattr(signal, signame),
lambda: asyncio.create_task(handler()))
setup()
asyncio.get_event_loop().run_forever()
如果您使用 run_forever
以外的其他方式启动事件循环,例如 asyncio.run(some_function())
,那么您需要将 loop.stop()
替换为设置主事件的代码协程等待。例如,如果它在服务器上等待 server.serve_forever()
,那么您会将服务器对象传递给 handler
并调用 server.close()
,依此类推。
我正在使用 discord.py 制作 Discord 机器人,有时我用来 运行 的脚本需要关闭机器人。当我在不使用信号处理程序的情况下关闭它时,有很多关于循环未关闭的错误,所以我添加了一个信号处理程序(使用下面的代码)并且在内部我需要调用 client.close()
和 client.logout()
,但问题是那些是异步函数,因此需要我等待它们,但我不能等待这些函数,因为信号处理程序不能是异步函数。
代码如下:
def handler():
print("Logging out of Discord Bot")
client.logout()
client.close()
sys.exit()
@client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
for signame in ('SIGINT', 'SIGTERM'):
client.loop.add_signal_handler(getattr(signal, signame),
lambda: asyncio.ensure_future(handler()))
有没有办法使用信号处理程序正确注销,或者至少只是让代码内部的警告和错误静音,这样就不会在控制台中打印错误。
您的方法是正确的 - 因为 add_signal_handler
需要一个普通函数而不是异步函数,您确实需要调用 ensure_future
(或其堂兄弟 create_task
)来在事件循环中向 运行 提交一个异步函数。下一步是实际使 handler
异步,并且 await
它调用的协程:
async def handler():
print("Logging out of Discord Bot")
await client.logout()
await client.close()
asyncio.get_event_loop().stop()
请注意,我将 sys.exit()
更改为显式停止事件循环,因为 asyncio 对从回调中间调用的 sys.exit()
反应不佳(它捕获 SystemExit
异常并抱怨未检索的异常)。
由于我没有discord可以测试,所以我通过更改注销和睡眠关闭来测试它:
import asyncio, signal
async def handler():
print("sleeping a bit...")
await asyncio.sleep(0.2)
print('exiting')
asyncio.get_event_loop().stop()
def setup():
loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
loop.add_signal_handler(getattr(signal, signame),
lambda: asyncio.create_task(handler()))
setup()
asyncio.get_event_loop().run_forever()
如果您使用 run_forever
以外的其他方式启动事件循环,例如 asyncio.run(some_function())
,那么您需要将 loop.stop()
替换为设置主事件的代码协程等待。例如,如果它在服务器上等待 server.serve_forever()
,那么您会将服务器对象传递给 handler
并调用 server.close()
,依此类推。