python - 使用 Trio 在屏幕上进行异步图像搜索
问题描述
在给定时间在同一屏幕截图中搜索大量图像时,我正在尝试调整此模块以支持异步执行。我对异步编码有点陌生,经过大量研究后,我选择了 Trio 来做这件事(因为它很棒而且很容易)。
重点是:
- 该函数接收图像路径列表
- 在每次迭代中,它都会截取屏幕截图并尝试在数组中查找图像(如果我们不为数组中的每次尝试都截取新的屏幕截图,这会更好地提高性能)
- 如果找到,则返回图像的路径和坐标
- 重新做一遍,因为屏幕上可能会出现一些图像
我将在另一个支持与 Trio 异步的项目中使用它,这就是我尝试转换它的原因。
这是我的尝试:
def image_search(image, precision=0.8, pil=None):
if pil is None:
pil = pyautogui.screenshot()
if is_retina:
pil.thumbnail((round(pil.size[0] * 0.5), round(pil.size[1] * 0.5)))
return most_probable_location(pil, image, precision)
async def multiple_image_search_loop(images, interval=0.1, timeout=None, precision=0.8):
async def do_search():
while True:
pil = pyautogui.screenshot()
for image in images:
if pos := image_search(image, precision, pil):
return {
"position": pos,
"image": image
}
await trio.sleep(interval)
if timeout:
with trio.fail_after(timeout):
return await do_search()
else:
return await do_search()
尽管代码看起来正确,但我觉得我错过了异步代码的要点。这一切都可以以同步的方式完成,我觉得我没有做任何改变。
如果性能没有差异,那还不错,因为关键是要使此功能在异步上下文中有用,而不会在搜索图像的整个过程中阻塞,但是如果我可以优化一些东西,那肯定会更好.
也许如果不是awaiting
在搜索所有图像之后我image_search()
通过调用trio.sleep()
并在主要功能上打开一个托儿所会更好?(trio.start_soon()
对数组中的每个图像使用其中的方法)。这会减少对我将要使用的其他项目的阻碍,但是查找图像需要更多时间,对吗?
解决方案
Trio 不会像这样直接并行化 CPU 绑定代码。作为一个“异步框架”意味着它只使用一个 CPU 线程,同时并行化 I/O 和网络操作。如果您插入一些对 then 的调用,await trio.sleep(0)
这将使 Trio 将图像搜索与其他任务交错,但不会使图像搜索更快。
但是,您可能想要做的是使用单独的线程。我猜你的代码可能大部分时间都花在了opencv上,而opencv可能会放弃GIL?因此,使用线程可能会让您同时在多个 CPU 上运行代码,同时还可以让其他异步任务同时运行。要像这样管理线程,Trio 允许您在线程await trio.to_thread.run_sync(some_sync_function, *args)
中运行some_sync_function(*args)
。如果您在托儿所中同时运行多个这样的调用,那么您将使用多个线程。
但是,使用线程需要注意一个主要问题:一旦trio.to_thread.run_sync
调用开始,它就不能被取消,因此超时等直到调用结束后才会生效。要解决此问题,您可能需要确保各个呼叫不会阻塞太久。
另外,关于风格的旁注:为 Trio 制作的函数通常不接受timeout=
这样的参数,因为如果用户想要添加超时,他们可以with
自己在函数周围编写块,就像传递参数一样容易。因此,这样您就不必到处乱用超时参数的 API。