RuntimeError: got Future <Future pending> attached to a different loop 协程异步函数asyncio.Queue()消息传递报错

1 问题描述

        当我在一个循环事件中创建三个异步函数时,其中两个作为生产者,余下一个作为消费者进行无限循环运行,他们之间通过异步消息队列 asyncio.Queue() 进行通信,代码如下:

import asyncio
from asyncio import sleep

queue1 = asyncio.Queue()


async def run_async_tasks():
    task1 = asyncio.create_task(producer_task_1())
    task2 = asyncio.create_task(producer_task_2())
    task3 = asyncio.create_task(consumer_task())
    await asyncio.gather(task1, task2, task3)


async def producer_task_1():
    item = ("message", "message from producer_task_1")
    while True:
        await queue1.put(item)
        print("producer_task_1:produce message")
        await sleep(2)


async def producer_task_2():
    item = ("message", "message from producer_task_2")
    while True:
        await queue1.put(item)
        print("producer_task_2:produce message")
        await sleep(4)


async def consumer_task():
    while True:
        info = await queue1.get()
        print(f"consume message: {info}")


asyncio.run(run_async_tasks())

很遗憾的是,发生了如下报错:

RuntimeError: Task <Task pending name='Task-4' coro=<consumer_task() running at E:\harrylin\pythonCode\pnt-server\test\queueTest.py:40> cb=[_gather.<locals>._done_callback() at D:\ProgramData\Anaconda3\envs\test03\lib\asyncio\tasks.py:767]> got Future <Future pending> attached to a different loop

2 问题分析        

作者反复确认代码,此三个异步任务是在同一个循环中的,为啥会报错 different loop 。通过分析发现,这个错误信息并非是说这三个异步任务不在同一个循环中,而是消息队列不在这三个异步任务的循环中。由于消息队列的初始化是在异步任务外部进行,可能会导致该消息队列被绑定到与异步任务不同的事件循环。这是因为在  asyncio.run(run_async_tasks()) 调用之前,全局变量初始化的队列可能没有被绑定到任何事件循环,或者被绑定到默认的事件循环。

3 问题解决

知道问题的原因后,要解决此问题也简单,只要确保消息队列在异步函数内部初始化,并在同一个事件循环中访问即可。根据以上思想,作者给出两个解决方案:

3.1 全局变量方案

方案1 消息队列依然作为全局变量,但在异步函数中进行初始化

import asyncio
from asyncio import sleep

queue1 = None  # 全局变量声明定义,但不进行初始化


async def run_async_tasks():
    global queue1  # 声明全局变量
    # 在异步函数中进行初始化,保证异步函数执行时才被初始化,从而能够绑定在同一个循环事件中
    queue1 = asyncio.Queue()  
    task1 = asyncio.create_task(producer_task_1())
    task2 = asyncio.create_task(producer_task_2())
    task3 = asyncio.create_task(consumer_task())
    await asyncio.gather(task1, task2, task3)

# 以下代码保持不变
async def producer_task_1():
    item = ("message", "message from producer_task_1")
    while True:
        await queue1.put(item)
        print("producer_task_1:produce message")
        await sleep(2)


async def producer_task_2():
    item = ("message", "message from producer_task_2")
    while True:
        await queue1.put(item)
        print("producer_task_2:produce message")
        await sleep(4)


async def consumer_task():
    while True:
        info = await queue1.get()
        print(f"consume message: {info}")


asyncio.run(run_async_tasks())

3.2 局部变量方案

方案2 消息队列作为全局变量,在 async def run_async_tasks() 中进行声明和初始化,并作为入参传递到各个异步任务中。优化后的全量代码如下:

但在异步函数中进行初始化

import asyncio
from asyncio import sleep


async def run_async_tasks():
    queue1 = asyncio.Queue()
    task1 = asyncio.create_task(producer_task_1(queue1))
    task2 = asyncio.create_task(producer_task_2(queue1))
    task3 = asyncio.create_task(consumer_task(queue1))
    await asyncio.gather(task1, task2, task3)


async def producer_task_1(queue1):
    item = ("message", "message from producer_task_1")
    while True:
        await queue1.put(item)
        print("producer_task_1:produce message")
        await sleep(2)


async def producer_task_2(queue1):
    item = ("message", "message from producer_task_2")
    while True:
        await queue1.put(item)
        print("producer_task_2:produce message")
        await sleep(4)


async def consumer_task(queue1):
    while True:
        info = await queue1.get()
        print(f"consume message: {info}")


asyncio.run(run_async_tasks())

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值