Asyncio Queue等到它满了之后再返回一些东西

时间:2018-03-17 06:15:06

标签: python-3.x python-asyncio cv2

我与asyncio.Queue有一个奇怪的问题 - 在没有可用的情况下尽快返回一个项目,队列会等到它已满,然后再返回任何内容。我意识到,当使用队列存储从cv2.VideoCapture收集的帧时,队列的maxsize越大,在屏幕上显示任何内容所需的时间越长,然后,它看起来像一个序列收集到队列中的所有帧。
这是一个功能,一个错误,还是我只是使用这个错误?
无论如何,这是我的代码

import asyncio  
import cv2  
import numpy as np  


async def collecting_loop(queue):  
    print("cl")  
    cap = cv2.VideoCapture(0)  
    while True:  
        _, img = cap.read()  
        await queue.put(img)  


async def processing_loop(queue):  
    print("pl")  
    await asyncio.sleep(0.1)  
    while True:  
        img = await queue.get()  
        cv2.imshow('img', img)  
        cv2.waitKey(5)  


async def main(e_loop):  
    print("running main")  
    queue = asyncio.Queue(loop=e_loop, maxsize=10)
    await asyncio.gather(collecting_loop(queue), processing_loop(queue))


loop = asyncio.get_event_loop()   
try:   
    loop.run_until_complete(main(e_loop=loop))   
except KeyboardInterrupt:   
    pass   
finally:   
    loop.close()   

2 个答案:

答案 0 :(得分:1)

问题是await q.put() 在每次通话时都不会将切换到另一个任务。实际上只有当队列完全状态转换挂起时才会插入新值。

插入await asyncio.sleep(0) 强制任务切换。 与多线程代码file.read()一样,不强制执行OS线程切换,但time.sleep(0)执行操作。

这样的误解对于新手很常见,昨天我已经讨论了非常类似的问题,请参阅github issue

<强> P.S。

您的代码实际上有更糟糕的问题:您从异步函数调用阻止同步代码,这不是asyncio的工作方式。

如果还没有异步OpenCV API,那么 应该在一个单独的线程中运行OpenCV函数。

已经提到janus可以帮助在同步和异步代码之间传递数据。

答案 1 :(得分:1)

  

队列获取者在队列填满之前没有醒来]一个功能,一个错误,还是我只是使用了这个错误?

你使用它错了,但是以微妙的方式。正如Andrew解释的那样,queue.put不保证任务切换,并且收集程序协程只运行阻塞代码和queue.put。尽管封锁很短,但asyncio并不知道这一点,并且认为你是在一个非常紧密的循环中调用queue.put。在队列填满之前,队列获取者根本没有机会运行。

集成asyncio和cv的正确方法是在单独的线程中运行cv代码并让asyncio事件循环等待它完成。 run_in_executor方法非常简单:

async def collecting_loop(queue):  
    print("cl")  
    loop = asyncio.get_event_loop()
    cap = cv2.VideoCapture(0)  
    while True:  
        _, img = await loop.run_in_executor(None, cap.read)
        await queue.put(img)

run_in_executor将在等待新帧时自动挂起收集器协程,允许及时处理排队的帧。