加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

python – asyncio CancelledError和KeyboardInterrupt

发布时间:2020-12-20 10:33:36 所属栏目:Python 来源:网络整理
导读:我正在尝试两种方法来阻止无限循环运行: supervisor_1:以编程方式取消任务 supervisor_2:使用Ctrl C停止任务 虽然supervisor_2在中断时不会抛出任何错误,但我无法让supervisor_1获得任务被销毁但它正在等待!知道为什么吗? 这是代码: import asyncioimp
我正在尝试两种方法来阻止无限循环运行:

> supervisor_1:以编程方式取消任务
> supervisor_2:使用Ctrl C停止任务

虽然supervisor_2在中断时不会抛出任何错误,但我无法让supervisor_1获得任务被销毁但它正在等待!知道为什么吗?

这是代码:

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上不时出现错误:

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

if __name__ == '__main__':
    redis_conn = aioredis.create_pool(('localhost',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()

解决方法

task.cancel()本身并没有完成任务:它只是告诉任务应该在其中引发CancelledError并立即返回.您应该调用它并等待任务实际被取消(同时它会引发CancelledError).

你也不应该在任务中压制CancelledError.

阅读this answer,我试图展示处理任务的不同方式.例如,要取消某个任务并等待它取消,您可以执行以下操作:

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.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读