Python 请解释一下;任务已被销毁,但它处于挂起状态&引用;
Python 3.4.2 我正在学习asyncio,我用它来连续监听IPC总线,而gbulb监听DBU 一些旁注: 因此,我创建了一个函数Python 请解释一下;任务已被销毁,但它处于挂起状态&引用;,python,python-3.x,python-3.4,python-asyncio,Python,Python 3.x,Python 3.4,Python Asyncio,Python 3.4.2 我正在学习asyncio,我用它来连续监听IPC总线,而gbulb监听DBU 一些旁注: 因此,我创建了一个函数listen\u to\u ipc\u channel\u layer,它持续侦听ipc通道上的传入消息,并将消息传递给消息处理程序 我也在听SIGTERM和SIGINT。因此,当我向运行您在底部找到的代码的python进程发送SIGTERM时,脚本应该优雅地终止 问题 …我收到以下警告: got signal 15: exit Task was destro
listen\u to\u ipc\u channel\u layer
,它持续侦听ipc通道上的传入消息,并将消息传递给消息处理程序
我也在听SIGTERM和SIGINT。因此,当我向运行您在底部找到的代码的python进程发送SIGTERM时,脚本应该优雅地终止
问题
…我收到以下警告:
got signal 15: exit
Task was destroyed but it is pending!
task: <Task pending coro=<listen_to_ipc_channel_layer() running at /opt/mainloop-test.py:23> wait_for=<Future cancelled>>
Process finished with exit code 0
我仍然对asyncio了解很少,但我想我知道发生了什么。在等待asyncio.sleep(0.1)
的yield时,信号处理程序捕获了SIGTERM,并在该过程中调用task.cancel()
抛出的问题:这是否应该触发中的取消错误,而True:
循环?
(因为它不是,但我就是这样理解的)
最后调用loop.stop()
,它停止循环,而不等待从asyncio.sleep(0.1)
产生返回结果,甚至整个协程侦听ipc\U通道层
如果我错了,请纠正我
我认为我需要做的唯一一件事是让我的程序等待asyncio.sleep(0.1)
的结果返回和/或协同路由来打破while循环并完成
我相信我混淆了很多事情。请帮我弄清楚这些事情,这样我就可以知道如何在没有警告的情况下优雅地关闭事件循环。问题在于取消任务后立即关闭循环。作为
“这将安排在下一个周期通过事件循环将取消的错误抛出到包装的协同程序中。”
以这段代码为例:
import asyncio
import signal
async def pending_doom():
await asyncio.sleep(2)
print(">> Cancelling tasks now")
for task in asyncio.Task.all_tasks():
task.cancel()
print(">> Done cancelling tasks")
asyncio.get_event_loop().stop()
def ask_exit():
for task in asyncio.Task.all_tasks():
task.cancel()
async def looping_coro():
print("Executing coroutine")
while True:
try:
await asyncio.sleep(0.25)
except asyncio.CancelledError:
print("Got CancelledError")
break
print("Done waiting")
print("Done executing coroutine")
asyncio.get_event_loop().stop()
def main():
asyncio.async(pending_doom())
asyncio.async(looping_coro())
loop = asyncio.get_event_loop()
for sig in (signal.SIGINT, signal.SIGTERM):
loop.add_signal_handler(sig, ask_exit)
loop.run_forever()
# I had to manually remove the handlers to
# avoid an exception on BaseEventLoop.__del__
for sig in (signal.SIGINT, signal.SIGTERM):
loop.remove_signal_handler(sig)
if __name__ == '__main__':
main()
注意ask\u exit
取消任务,但不停止
循环,在下一个循环循环时
停止循环。如果取消,则输出为:
Executing coroutine
Done waiting
Done waiting
Done waiting
Done waiting
^CGot CancelledError
Done executing coroutine
注意pending_doom
是如何在循环结束后立即取消和停止循环的。如果让它一直运行,直到pending_doom
coroutines从睡眠中醒来,您可以看到与您得到的相同的警告:
Executing coroutine
Done waiting
Done waiting
Done waiting
Done waiting
Done waiting
Done waiting
Done waiting
>> Cancelling tasks now
>> Done cancelling tasks
Task was destroyed but it is pending!
task: <Task pending coro=<looping_coro() running at canceling_coroutines.py:24> wait_for=<Future cancelled>>
执行协同程序
等待结束
等待结束
等待结束
等待结束
等待结束
等待结束
等待结束
>>正在取消任务
>>已完成取消任务
任务已被销毁,但它处于挂起状态!
任务:
问题的意思是循环没有时间完成所有任务
这将安排在事件循环的下一个周期将CanceledError抛出到包装的协同程序中
在您的方法中,没有机会进行“下一个循环”。为了使其正确,您应该将停止操作移动到一个单独的非循环协程中,以使您的循环有机会完成
第二个重要的事情是取消错误
与Future.cancel()不同,这并不保证任务将被取消:异常可能被捕获并处理,从而延迟任务的取消或完全阻止取消。该任务还可能返回值或引发其他异常
调用此方法后,cancelled()将不会立即返回True(除非任务已取消)。当包装的协同程序终止并出现CancelleError异常时(即使未调用cancel()),任务将被标记为已取消
因此,在清理之后,您的协同程序必须引发CancelledError
以标记为已取消
使用额外的协程来停止循环不是问题,因为它不是循环的,并且可以在执行后立即完成
def main():
loop = asyncio.get_event_loop()
asyncio.ensure_future(listen_to_ipc_channel_layer())
for sig in (signal.SIGINT, signal.SIGTERM):
loop.add_signal_handler(sig, ask_exit)
loop.run_forever()
print("Close")
loop.close()
@asyncio.coroutine
def listen_to_ipc_channel_layer():
while True:
try:
print("Running")
yield from asyncio.sleep(0.1)
except asyncio.CancelledError as e:
print("Break it out")
raise e # Raise a proper error
# Stop the loop concurrently
@asyncio.coroutine
def exit():
loop = asyncio.get_event_loop()
print("Stop")
loop.stop()
def ask_exit():
for task in asyncio.Task.all_tasks():
task.cancel()
asyncio.ensure_future(exit())
if __name__ == "__main__":
main()
发生这种情况的原因由@Yeray Diaz Diaz解释
在我的例子中,我想取消第一次完成后未完成的所有任务,因此我最终取消了额外的作业,然后使用循环。\u run\u once()
再次运行循环并允许它们停止:
loop = asyncio.get_event_loop()
job = asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
tasks_finished,tasks_pending, = loop.run_until_complete(job)
tasks_done = [t for t in tasks_finished if t.exception() is None]
if tasks_done == 0:
raise Exception("Failed for all tasks.")
assert len(tasks_done) == 1
data = tasks_done[0].result()
for t in tasks_pending:
t.cancel()
t.cancel()
while not all([t.done() for t in tasks_pending]):
loop._run_once()
我有这个消息,我相信它是由未决任务的垃圾收集引起的。Python开发人员正在讨论在asyncio中创建的任务是否应该创建强引用,并决定不应该创建强引用(在研究了这个问题两天后,我强烈反对!…请参见此处的讨论)
我为自己创建了这个实用程序,以便对任务进行强引用并自动清理它(仍然需要彻底测试它)
在我看来,只要任务处于活动状态,它们就应该具有很强的参考价值!但是asyncio不能为您做到这一点,因此一旦gc发生并进行长时间的调试,您可能会遇到一些不好的意外情况。关于与信号处理程序相关的异常:您也可以通过在最后调用loop.close()
来避免此异常。@maarten:不,我不能:S我真的希望我可以。。。我所有的任务都取消了。。。
loop = asyncio.get_event_loop()
job = asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
tasks_finished,tasks_pending, = loop.run_until_complete(job)
tasks_done = [t for t in tasks_finished if t.exception() is None]
if tasks_done == 0:
raise Exception("Failed for all tasks.")
assert len(tasks_done) == 1
data = tasks_done[0].result()
for t in tasks_pending:
t.cancel()
t.cancel()
while not all([t.done() for t in tasks_pending]):
loop._run_once()
import asyncio
#create a strong reference to tasks since asyncio doesn't do this for you
task_references = set()
def register_ensure_future(coro):
task = asyncio.ensure_future(coro)
task_references.add(task)
# Setup cleanup of strong reference on task completion...
def _on_completion(f):
task_references.remove(f)
task.add_done_callback(_on_completion)
return task