Python中的多线程缩略图生成

时间:2011-04-28 12:39:50

标签: python multithreading image-processing

我想递归一个图像目录并为每个图像生成缩略图。我的机器上有12个可用的核心。什么是利用它们的好方法?我没有太多编写多线程应用程序的经验,所以任何简单的示例代码都是值得赞赏的。提前谢谢。

3 个答案:

答案 0 :(得分:7)

摘要

使用进程,而不是线程,因为由于GIL,Python对CPU密集型线程效率低下。 多处理的两种可能解决方案是:

multiprocessing模块

如果您使用内部缩略图制作工具(例如PIL),则首选此选项。只需编写缩略图制作功能,并行启动12。当其中一个进程完成后,在其插槽中运行另一个进程。

改编自Python文档,这里的脚本应该使用12个核心:

from multiprocessing import Process
import os

def info(title):  # For learning purpose, remove when you got the PID\PPID idea
    print title
    print 'module:', __name__
    print 'parent process:', os.getppid(), 
    print 'process id:', os.getpid()

def f(name):      # Working function
    info('function f')
    print 'hello', name

if __name__ == '__main__':
    info('main line')
    processes=[Process(target=f, args=('bob-%d' % i,)) for i  in range(12)]
    [p.start() for p in processes]
    [p.join()  for p in processes]

附录:使用multiprocess.pool()

根据soulman的评论,您可以使用提供的流程拉动。

我已经调整了multiprocessing manual中的一些代码。请注意,您可能应该使用multiprocessing.cpu_count()而不是4来自动确定CPU的数量。

from multiprocessing import Pool
import datetime

def f(x):  # You thumbnail maker function, probably using some module like PIL
    print '%-4d: Started at %s' % (x, datetime.datetime.now())
    return x*x

if __name__ == '__main__':
    pool = Pool(processes=4)              # start 4 worker processes
    print pool.map(f, range(25))          # prints "[0, 1, 4,..., 81]"

给出(注意打印输出没有严格订购!):

0   : Started at 2011-04-28 17:25:58.992560
1   : Started at 2011-04-28 17:25:58.992749
4   : Started at 2011-04-28 17:25:58.992829
5   : Started at 2011-04-28 17:25:58.992848
2   : Started at 2011-04-28 17:25:58.992741
3   : Started at 2011-04-28 17:25:58.992877
6   : Started at 2011-04-28 17:25:58.992884
7   : Started at 2011-04-28 17:25:58.992902
10  : Started at 2011-04-28 17:25:58.992998
11  : Started at 2011-04-28 17:25:58.993019
12  : Started at 2011-04-28 17:25:58.993056
13  : Started at 2011-04-28 17:25:58.993074
14  : Started at 2011-04-28 17:25:58.993109
15  : Started at 2011-04-28 17:25:58.993127
8   : Started at 2011-04-28 17:25:58.993025
9   : Started at 2011-04-28 17:25:58.993158
16  : Started at 2011-04-28 17:25:58.993161
17  : Started at 2011-04-28 17:25:58.993179
18  : Started at 2011-04-28 17:25:58.993230
20  : Started at 2011-04-28 17:25:58.993233
19  : Started at 2011-04-28 17:25:58.993249
21  : Started at 2011-04-28 17:25:58.993252
22  : Started at 2011-04-28 17:25:58.993288
24  : Started at 2011-04-28 17:25:58.993297
23  : Started at 2011-04-28 17:25:58.993307
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 
 289, 324, 361, 400, 441, 484, 529, 576]

subprocess模块

subprocess模块对于运行外部流程非常有用,因此如果您打算使用imagemagick convert之类的外部缩略图制作工具,则首选。代码示例:

import subprocess as sp

processes=[sp.Popen('your-command-here', shell=True, 
                    stdout=sp.PIPE, stderr=sp.PIPE) for i in range(12)]

现在,迭代进程。如果任何流程已完成(使用subprocess.poll()),请将其删除并在列表中添加新流程。

答案 1 :(得分:2)

与其他人一样,子进程通常比线程更好。 multiprocessing.Pool使您可以轻松使用任意数量的子进程,例如:

import os
from multiprocessing import Pool

def process_file(filepath):
    [if filepath is an image file, resize it]

def enumerate_files(folder):
    for dirpath, dirnames, filenames in os.walk(folder):
       for fname in filenames:
           yield os.path.join(dirpath, fname)

if __name__ == '__main__':
    pool = Pool(12) # or omit the parameter to use CPU count
    # use pool.map() only for the side effects, ignore the return value
    pool.map(process_file, enumerate_files('.'), chunksize=1)

与每个子进程通信相比,如果每个文件操作相对较慢,则chunksize = 1参数是有意义的。

答案 2 :(得分:1)

不要使用线程,它们太复杂了你想要的东西。相反,使用子进程库来生成在每个目录中工作的单独进程。

因此,您将拥有一个生成文件列表的主程序,然后开始从列表中弹出每个文件并将其提供给子进程。子进程将是一个简单的python程序,用于从输入图像生成缩略图。将生成的进程保存在有限的集合中的一些简单逻辑(例如11)可以防止您对机器进行分叉。

这允许操作系统处理谁在哪里运行等等的所有细节。