首页 > 解决方案 > 使用 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()对数组中的每个图像使用其中的方法)。这会减少对我将要使用的其他项目的阻碍,但是查找图像需要更多时间,对吗?

标签: pythonasync-awaitpython-trio

解决方案


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。


推荐阅读