首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >异步CancelledError与KeyboardInterrupt

异步CancelledError与KeyboardInterrupt
EN

Stack Overflow用户
提问于 2017-05-05 12:29:46
回答 1查看 11.4K关注 0票数 7

我正在尝试两种方法来阻止无限循环的运行:

  • supervisor_1:任务按程序取消
  • supervisor_2:任务用Ctrl+C停止

虽然supervisor_2不会在中断时抛出任何错误,但我不能从获取Task was destroyed but it is pending!中获得supervisor_1。知道为什么吗?

以下是代码:

代码语言:javascript
复制
import asyncio
import aioredis
from functools import partial



class Listener:
    def __init__(self, redis_conn):
        self.redis_conn = redis_conn

    async def forever(self, loop_name):
        counter = 0
        try:
            while True:
                print('{}: {}'.format(loop_name, counter))
                counter += 1
                await asyncio.sleep(1)
        except asyncio.CancelledError:
            print('Task Cancelled')
            self.redis_conn.close()
            await self.redis_conn.wait_closed()


async def supervisor_1(redis_conn):
    redis_conn = await redis_conn

    l = Listener(redis_conn)

    task = asyncio.ensure_future(
        asyncio.gather(l.forever('loop_1'), 
                       l.forever('loop_2')))
    await asyncio.sleep(2)
    task.cancel()


async def supervisor_2(redis_conn):
    redis_conn = await redis_conn

    l = Listener(redis_conn)
    await asyncio.gather(l.forever('loop_1'), 
                         l.forever('loop_2'))


if __name__ == '__main__':
    redis_conn = aioredis.create_pool(('localhost', 5003), db=1)

    loop = asyncio.get_event_loop()
    run = partial(supervisor_2, redis_conn=redis_conn)
    task = asyncio.ensure_future(run())
    try:
        loop.run_until_complete(task)
    except KeyboardInterrupt:
        print('Interruped !')
        task.cancel()
        loop.run_forever()
    finally:
        loop.close()

@update

多亏@Gerasimov,这里有一个解决问题的版本,但在KeyboardInterrupt上仍然不时地引发错误:

代码语言:javascript
复制
async def supervisor(redis_conn):
    redis_conn = await redis_conn

    l = Listener(redis_conn)

    task = asyncio.ensure_future(
        asyncio.gather(l.forever('loop_1'), 
                       l.forever('loop_2'))
    )
    await asyncio.sleep(10)
    task.cancel()
    with suppress(asyncio.CancelledError):
        await task

async def kill_tasks():
    pending = asyncio.Task.all_tasks()
    for task in pending:
        task.cancel()
        with suppress(asyncio.CancelledError):
            await task 

代码语言:javascript
复制
if __name__ == '__main__':
    redis_conn = aioredis.create_pool(('localhost', 5003), db=1)

    loop = asyncio.get_event_loop()
    run = partial(supervisor, redis_conn=redis_conn)
    task = asyncio.ensure_future(run())
    try:
        loop.run_until_complete(task)
    except KeyboardInterrupt:
        print('Interruped !')
        loop.run_until_complete(kill_tasks())
    finally:
        loop.close()
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-05-05 16:59:41

task.cancel()本身并没有完成任务:它只是对任务说,CancelledError应该在任务中被启动并立即返回。当任务实际被取消时,您应该调用它并等待它(虽然它会引发CancelledError)。

您也不应该在任务中禁止CancelledError

阅读this answer,在这里我试图展示处理任务的不同方法。例如,要取消某些任务并等待其取消,您可以:

代码语言:javascript
复制
from contextlib import suppress


task = ...  # remember, task doesn't suppress CancelledError itself

task.cancel()  # returns immediately, we should await task raised CancelledError.

with suppress(asyncio.CancelledError):
    await task  # or loop.run_until_complete(task) if it happens after event loop stopped

# Now when we awaited for CancelledError and handled it, 
# task is finally over and we can close event loop without warning.
票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43804993

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档