Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 异步IO取消错误和键盘中断_Python_Python Asyncio - Fatal编程技术网

Python 异步IO取消错误和键盘中断

Python 异步IO取消错误和键盘中断,python,python-asyncio,Python,Python Asyncio,我正在尝试两种方法来阻止无限循环运行: 主管\u 1:按程序取消任务 主管2:使用Ctrl+C停止任务 虽然supervisor_2在中断时不会抛出任何错误,但我无法从获取中获取supervisor_1任务已被销毁,但它处于挂起状态。知道为什么吗 代码如下: import asyncio import aioredis from functools import partial class Listener: def __init__(self, redis_conn):

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

  • 主管\u 1:按程序取消任务
  • 主管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()
@更新

感谢@Gerasimov,这里有一个版本可以解决这个问题,但不知何故仍然会不时在键盘中断上出现错误:

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 

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

您也不应该在任务内部抑制
cancelederror

阅读我试图展示处理任务的不同方式的地方。例如,要取消某些任务并等待其取消,您可以执行以下操作:

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.

谢谢你的链接。我用我对补丁的理解更新了我的答案。但是仍然会出错(但不总是像以前一样)@Orelus,还是以前的错误吗?尝试移动
循环。在
loop.close()之前,运行\u直到\u完成(kill\u tasks())
到finally块中。这可能会解决问题。我不确定您的
run()
coroutine会做什么,但当它完成时,可能会发生这种情况,但有些任务并非如此:在这种情况下,在关闭事件循环时,即使未发生
键盘中断
,也会收到警告。请注意使用这种方法的一个缺点:如果调用任务本身在等待子任务清理和退出时被取消,则
抑制()
调用将取代
取消错误
,事实上,
等待任务
可能会提前中止。看起来当前的asyncio(至少从3.8开始)可能无法避免这种情况。
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.