使用asyncio进行PIL和阻止调用

问题描述 投票:2回答:1

我有一个asyncio应用程序,它使用来自aiohttp的服务器和与asyncio.open_connection()的异步套接字

我的代码包含来自PIL库的一些阻塞调用

Image.save()
Image.resize()
  1. 即使呼叫没有阻塞太多时间,但是,如果我使用这些阻止呼叫,我的网络服务器是否会冻结?更准确地说,由于阻塞代码,事件循环是否可能会丢失事件?
  2. 如果是,那些与asyncio集成的功能的替代品是什么? PIL没有asyncio版本。
  3. 一般来说,什么被认为是asyncio中的“阻止代码”?除了明显的操作,如套接字,读取文件等。 例如,os.path.join()被认为是好的吗?在numpy阵列上工作怎么样?
python numpy python-imaging-library python-asyncio blocking
1个回答
4
投票

如果我使用这些阻止调用,我的Web服务器可以冻结吗?更准确地说,由于阻塞代码,事件循环是否可能会丢失事件?

服务器将精确冻结执行图像功能的时间。您不会错过任何事件,但所有事件处理将在图像功能执行时延迟。

冻结事件循环是一个糟糕的情况 - 你应该避免它。

如果是,那些与asyncio集成的功能的替代品是什么? PIL没有asyncio版本。

避免冻结事件循环的最简单和通用的方法 - 使用asyncio.run_in_executor在另一个线程或另一个进程中执行阻塞函数。代码片段显示了如何执行此操作并包含何时使用进程或线程的详细说明:

def blocking_io():
    # File operations (such as logging) can block the
    # event loop: run them in a thread pool.
    with open('/dev/urandom', 'rb') as f:
        return f.read(100)

def cpu_bound():
    # CPU-bound operations will block the event loop:
    # in general it is preferable to run them in a
    # process pool.
    return sum(i * i for i in range(10 ** 7))

我只想补充说,对于每个CPU绑定操作,进程池可能并不总是很好的解决方案。如果您的映像功能不需要花费太多时间(或者特别是如果您的服务器没有多个处理器核心),那么在线程中运行它们可能仍然会更高效。

一般来说,什么被认为是asyncio中的“阻止代码”?除了明显的操作,如套接字,读取文件等。例如,os.path.join()被认为是好的吗?在numpy阵列上工作怎么样?

粗略地说任何函数都是阻塞的:它会阻塞事件循环一段时间。但像os.path.join这样的许多功能花费的时间很少,所以它们不是问题,我们不称它们为“阻塞”。

当执行时间(和事件循环冻结)成为问题时,很难说确切的限制,特别是考虑到不同硬件的时间会有所不同。我有偏见的建议 - 如果您的代码在将控制权返回到事件循环之前需要(或可能需要)> 50 ms,请考虑阻止并使用run_in_executor

UPD:

谢谢,使用一个事件循环(主线程),并使用另一个使用相同循环添加任务的线程是否有意义?

我不确定你的意思,但我认为不是。我们需要另一个线程来运行一些工作,而不是在那里添加任务。

我需要一些方法让线程在图像处理完成后通知主线程

等待run_in_executor的结果或开始任务。 run_in_executor - 是一个协程,它在后台线程中执行某些操作而不会阻塞事件循环。

它看起来像这样:

thrad_pool = ThreadPoolExecutor()


def process_image(img):
    # all stuff to process image here
    img.save()
    img.resize()


async def async_image_process(img):
    await loop.run_in_executor(
        thread_pool, 
        partial(process_image, img)
    )


async def handler(request):

    asyncio.create_task(
        async_image_process(img)
    )
    # we use task to return response immediately,
    # read https://stackoverflow.com/a/37345564/1113207

    return web.Response(text="Image processed without blocking other requests")
© www.soinside.com 2019 - 2024. All rights reserved.