我与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()
答案 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
将在等待新帧时自动挂起收集器协程,允许及时处理排队的帧。