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。设置异常处理程序
忽略这些消息。无论哪种方式,都需要额外的代码。