Python 3.x 处理永远不会在python3异步中终止的任务
有时异步任务没有有意义的终止条件——例如,在下面的程序中,“速率限制器”任务以固定速率永远在队列上生成令牌流Python 3.x 处理永远不会在python3异步中终止的任务,python-3.x,python-asyncio,Python 3.x,Python Asyncio,有时异步任务没有有意义的终止条件——例如,在下面的程序中,“速率限制器”任务以固定速率永远在队列上生成令牌流 import asyncio import sys @asyncio.coroutine def rate_limiter(queue, rate): """Push tokens to QUEUE at a rate of RATE per second.""" delay = 1/rate while True: yield from asy
import asyncio
import sys
@asyncio.coroutine
def rate_limiter(queue, rate):
"""Push tokens to QUEUE at a rate of RATE per second."""
delay = 1/rate
while True:
yield from asyncio.sleep(delay)
yield from queue.put(None)
@asyncio.coroutine
def do_work(n, rate):
for i in range(n):
yield from rate.get()
sys.stdout.write("job {}\n".format(i))
def main():
loop = asyncio.get_event_loop()
rate = asyncio.Queue()
rltask = loop.create_task(rate_limiter(rate, 10))
wtask = loop.create_task(do_work(20, rate))
loop.run_until_complete(wtask)
main()
这个程序运行得很好,除了asyncio库认为在没有任何剩余的速率限制时扔掉rltask
是一个编程错误之外;你会收到这样的投诉
...
job 18
job 19
Task was destroyed but it is pending!
task: <Task pending coro=<rate_limiter() running at rl.py:9>
wait_for=<Future pending cb=[Task._wakeup()]>>
。。。
工作18
工作19
任务已被销毁,但它处于挂起状态!
任务:
(无论是否处于调试模式)
我可以用一个事件告诉rate\u limiter
corroutine打破它的循环,但这感觉像是额外的代码,没有真正的好处。在使用asyncio时,您应该如何处理这种情况
编辑:我不清楚:我要寻找的是类似于线程上的守护进程
标志的东西:这样我就不必等待某个特定的任务,理想情况下可以表示为任务本身的注释或其协程。我也愿意接受一个表明没有这种机制的答复。我已经知道了解决方法。.cancel()
将任务取消,然后等待它被取消,捕获外部的取消错误:
# vim: tabstop=4 expandtab
import asyncio
import sys
@asyncio.coroutine
def rate_limiter(queue, rate):
"""Push tokens to QUEUE at a rate of RATE per second."""
delay = 1/rate
while True:
yield from asyncio.sleep(delay)
yield from queue.put(None)
@asyncio.coroutine
def do_work(n, rate):
for i in range(n):
yield from rate.get()
sys.stdout.write("job {}\n".format(i))
def main():
loop = asyncio.get_event_loop()
rate = asyncio.Queue()
rltask = loop.create_task(rate_limiter(rate, 10))
wtask = loop.create_task(do_work(20, rate))
loop.run_until_complete(wtask)
rltask.cancel()
try:
loop.run_until_complete(rltask)
except asyncio.CancelledError:
...
loop.close()
main()
为避免“任务已销毁,但它已挂起!”警告,如果为相应的未来对象设置了虚拟结果,则可以在退出程序时将永不结束的协同程序标记为已完成:
#!/usr/bin/env python3.5
import asyncio
import itertools
from contextlib import closing, contextmanager
@contextmanager
def finishing(coro_or_future, *, loop=None):
"""Mark a never ending coroutine or future as done on __exit__."""
fut = asyncio.ensure_future(
coro_or_future, loop=loop) # start infinite loop
try:
yield
finally:
if not fut.cancelled():
fut.set_result(None) # mark as finished
async def never_ends():
for c in itertools.cycle('\|/-'):
print(c, end='\r', flush=True)
await asyncio.sleep(.3)
with closing(asyncio.get_event_loop()) as loop, \
finishing(never_ends(), loop=loop):
loop.run_until_complete(asyncio.sleep(3)) # do something else
它假定您的协同程序在进程退出之前不需要显式清理。在后一种情况下,定义一个明确的清理过程:提供可以调用的方法(例如,server.close()
,server.wait\u closed()
),或传递调用方在关机时应设置的事件(asyncio.event
),或引发异常(例如cancelederror
)
引入finishing()
的好处是检测bug,也就是说,除非警告被finishing()
调用显式静音,否则不应忽略警告。无关:在多线程情况下,a(在程序退出时死亡)@J.F.Sebastian对,这是相同的算法,转换为异步IO任务。据我所知,asyncio没有与守护进程线程等效的线程。您可以使用asyncio.Task.all_tasks()
和调用cancel()
或只调用rltask.cancel()
和在rate\u limitor()中中断cancelederror
@J.F.Sebastian,涉及的额外代码量与事件大致相同。未停止时。等待(延迟)
其中停止时是asyncio。事件可以模拟asyncio.sleep(延迟)
+取消错误
但它不会模拟更复杂的代码,在退出时需要清理(例如,通过显式协议(如server.close()
,server.wait\u closed()
)使用和-语句,如果需要完全关闭,效果会更好)。正如我在回答的评论中所说,这涉及到“额外的代码,没有真正的好处,”就像使用事件结束循环一样。您是否认为没有办法不必等待rltask
?您已经不必等待rltask
。唯一的问题是asyncio
任务实现作为一个变量。您可以将私有变量设置为False
,或者您可以将其忽略使用默认的异常处理程序w/BaseEventLoop。设置异常处理程序
忽略这些消息。无论哪种方式,都需要额外的代码。