我刚刚开始使用Python 3中的库concurrent.futures
来应用图像列表中的许多函数,以便处理这些图像并重新整形。
功能是resize(height, width)
和opacity(number)
。
另一方面,我有images()
函数可以生成类似文件的对象,
所以我尝试使用此代码并行处理我的图像:
import concurrent.futures
From mainfile import images
From mainfile import shape
def parallel_image_processing :
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
future = executor.submit(images)
for fileobject in future.result() :
future1 = executor.submit( shape.resize, fileobject, "65","85")
future2 = executor.submit( shape.opacity, fileobject, "0.5")
有人可以告诉我是否在正确的道路上实现这一目标吗?
答案 0 :(得分:1)
我建议让images
只返回路径,而不是打开文件对象:
def images():
...
yield os.path.join(image_dir[0], filename)
然后用这个:
from functools import partial
def open_and_call(func, filename, args=(), kwargs={}):
with open(filename, 'rb') as f:
return func(f, *args, **kwargs)
def parallel_image_processing():
resize_func = partial(open_and_call, shape.resize, args=("65", "85"))
opacity_func = partial(open_and_call, shape.opacity, args=("0.5"))
img_list = list(images())
with concurrent.futures.ProcessPoolExecutor(max_workers=5) as executor:
futures1 = executor.map(resize_func, img_list)
futures2 = executor.map(opacity_func, img_list)
concurrent.futures.wait([futures1, futures2])
if __name__ == "__main__":
# Make sure the entry point to the function that creates the executor
# is inside an `if __name__ == "__main__"` guard if you're on Windows.
parallel_image_processing()
如果您正在使用CPython(而不是像Jython那样没有GIL的替代实现),您不想使用ThreadPoolExecutor
,因为图像处理是CPU密集型的;由于GIL,在CPython中一次只能运行一个线程,所以如果你为用例使用线程,你实际上并没有做任何事情。相反,使用ProcessPoolExecutor
,它将使用进程而不是线程,完全避免使用GIL。请注意,这就是为什么我建议不从images
返回类似文件的对象 - 您无法将打开的文件句柄传递给工作进程。您必须在工作人员中打开文件。
为此,我们让executor
调用一个小填充函数(open_and_call
),它将在工作进程中打开文件,然后调用resize
/ {{ 1}}具有正确参数的函数。
我还使用opacity
代替executor.map
,以便我们可以为executor.submit
返回的每个项目调用resize
/ opacity
一个明确的循环。我使用images()
来使用functools.partial
调用带有多个参数的函数变得更容易(只允许你调用带有单个参数的函数)。
还没有必要在执行者中调用executor.map
,因为您无论如何都要等待其结果。只需将其称为普通功能即可。在调用images()
之前,我将images()
返回的生成器对象转换为list
。如果您担心内存使用情况,可以直接在每次map
来电中致电images()
,但如果没有,只需拨打map
一次就可以更快将其存储为列表。