Python 我在这里做多核编程对吗

Python 我在这里做多核编程对吗,python,multithreading,process,multicore,Python,Multithreading,Process,Multicore,我希望有一些守护程序,可以找到需要转换为web和thumb版本的图像。我认为python在这里可能有用,但我不确定我是否在这里做事情。我想同时转换8张照片,要转换的图像队列可能很长。我们在服务器上有几个内核,在一个新的进程中生成每个转换应该让操作系统利用可用的内核,事情会发展得更快,对吗?这是这里的关键点,从python生成一个进程,再次调用imagemagick的转换脚本,并希望事情比从python主线程运行一对一转换快一点 到目前为止,我只是开始测试。这是我的测试代码。它将创建20个任务(睡

我希望有一些守护程序,可以找到需要转换为web和thumb版本的图像。我认为python在这里可能有用,但我不确定我是否在这里做事情。我想同时转换8张照片,要转换的图像队列可能很长。我们在服务器上有几个内核,在一个新的进程中生成每个转换应该让操作系统利用可用的内核,事情会发展得更快,对吗?这是这里的关键点,从python生成一个进程,再次调用imagemagick的转换脚本,并希望事情比从python主线程运行一对一转换快一点

到目前为止,我只是开始测试。这是我的测试代码。它将创建20个任务(睡眠时间为1到5秒),并将这些任务分配给总共有5个线程的池

from multiprocessing import Process
from subprocess import call
from random import randrange
from threading import Thread
from Queue import Queue

class Worker(Thread):
    def __init__(self, tid, queue):
        Thread.__init__(self)
        self.tid = tid
        self.queue = queue
        self.daemon = True
        self.start()

    def run(self):
        while True:
            sec = self.queue.get()
            print "Thread %d sleeping for %d seconds\n\n" % (self.tid, sec)
            p = Process(target=work, args=(sec,))
            p.start()
            p.join()
            self.queue.task_done()

class WorkerPool:
    def __init__(self, num_workers):
        self.queue = Queue()
        for tid in range(num_workers):
            Worker(tid, self.queue)

    def add_task(self, sec):
        self.queue.put(sec)

    def complete_work(self):
        self.queue.join()

def work(sec):
    call(["sleep", str(sec)])

def main():
    seconds = [randrange(1, 5) for i in range(20)]
    pool = WorkerPool(5)
    for sec in seconds:
        pool.add_task(sec)
    pool.complete_work()

if __name__ == '__main__':
    main()
因此,我在服务器上运行此脚本:

johanhar@mamadev:~$ python pythonprocesstest.py
johanhar@mamadev:~$ ps -fux
然后我检查服务器上的进程:

johanhar@mamadev:~$ python pythonprocesstest.py
johanhar@mamadev:~$ ps -fux
ps
的结果在我看来是错误的。在我看来,在python下似乎发生了一些事情,但是在一个进程中发生了一些事情,因此即使服务器上有多个内核,转换次数越多(或者像本测试案例中那样休眠),转换速度也只会越慢

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
johanhar 24246  0.0  0.0  81688  1608 ?        S    13:44   0:00 sshd: johanhar@pts/28
johanhar 24247  0.0  0.0 108336  1832 pts/28   Ss   13:44   0:00  \_ -bash
johanhar 49753  0.6  0.0 530620  7512 pts/28   Sl+  15:14   0:00      \_ python pythonprocesstest.py
johanhar 49822  0.0  0.0 530620  6252 pts/28   S+   15:14   0:00          \_ python pythonprocesstest.py
johanhar 49824  0.0  0.0 100904   564 pts/28   S+   15:14   0:00          |   \_ sleep 4
johanhar 49823  0.0  0.0 530620  6256 pts/28   S+   15:14   0:00          \_ python pythonprocesstest.py
johanhar 49826  0.0  0.0 100904   564 pts/28   S+   15:14   0:00          |   \_ sleep 3
johanhar 49837  0.0  0.0 530620  6264 pts/28   S+   15:14   0:00          \_ python pythonprocesstest.py
johanhar 49838  0.0  0.0 100904   564 pts/28   S+   15:14   0:00          |   \_ sleep 3
johanhar 49846  0.0  0.0 530620  6264 pts/28   S+   15:14   0:00          \_ python pythonprocesstest.py
johanhar 49847  0.0  0.0 100904   564 pts/28   S+   15:14   0:00              \_ sleep 3

所以如果你还是不明白我的问题或者我的要求。这种方法可以称为“多核编程”吗?

我认为您误读了
ps
输出。我统计了4个不同的Python实例,每个实例原则上都可以分配给自己的核心。他们是否真的拥有自己的内核是多重处理中比较困难的一点


是的,有一个高级Python进程(PID 49753),它是子进程的父进程,但也有一个
bash
,它以类似的方式是子进程的父进程

我认为您误读了
ps
输出。我统计了4个不同的Python实例,每个实例原则上都可以分配给自己的核心。他们是否真的拥有自己的内核是多重处理中比较困难的一点


是的,有一个高级Python进程(PID 49753),它是子进程的父进程,但也有一个
bash
,它以类似的方式是子进程的父进程

Short&direct:是的,您正在多核上运行多个
convert
进程

更长&稍微间接:我不会称之为“多核编程”,即使它实际上是,因为这种措辞通常意味着在多核上运行程序的多个线程,而您没有这样做(至少在CPython中,python线程受GIL约束,不能在多核上同时运行)。此外,您不需要并行化python代码,因为这不是您的瓶颈(您将时间花在
convert
上,而不是用python代码)

如果您只想并行化
转换
,那么在python代码中甚至不需要任何线程或其他花哨的东西

python脚本可以被序列化并在照片中循环,生成新的转换过程,直到达到所需的数量。然后就坐在那里,等待其中一个完成并产生一个新的;根据需要对所有照片重复此操作


(但我同意线程比那种等待事件循环更自然、更优雅)

简短和直接:是的,您正在多核上运行多个
convert
进程

更长&稍微间接:我不会称之为“多核编程”,即使它实际上是,因为这种措辞通常意味着在多核上运行程序的多个线程,而您没有这样做(至少在CPython中,python线程受GIL约束,不能在多核上同时运行)。此外,您不需要并行化python代码,因为这不是您的瓶颈(您将时间花在
convert
上,而不是用python代码)

如果您只想并行化
转换
,那么在python代码中甚至不需要任何线程或其他花哨的东西

python脚本可以被序列化并在照片中循环,生成新的转换过程,直到达到所需的数量。然后就坐在那里,等待其中一个完成并产生一个新的;根据需要对所有照片重复此操作


(但我同意线程比等待事件循环更自然、更优雅)

您可以简化代码。如果工作是在子进程中完成的,则不需要多个Python进程。您可以使用
多处理.Pool
来限制并发子进程的数量:

#!/usr/bin/env python
import multiprocessing.dummy as mp # use threads
from random import randrange
from subprocess import check_call
from timeit import default_timer as timer

def info(msg, _print_lock=mp.Lock()): # a poor man's logging.info()
    with _print_lock: # avoid garbled output
        print("%s\t%s" % (mp.current_process().name, msg))

def work(sec):
    try: # wrap in try/except to avoid premature exit
        info("Sleeping for %d seconds" % (sec,))
        start = timer()
        check_call(["sleep", str(sec)])
    except Exception as e: # error
        return sec, timer() - start, e
    else: # success
        return sec, timer() - start, None

def main():
    work_items = (randrange(1, 5) for i in range(20)) # you can use generator
    pool = mp.Pool(5) # pool of worker threads
    for result in pool.imap_unordered(work, work_items):
        info("expected %s, got %s, error %s" % result)
    pool.close()
    pool.join()

if __name__ == '__main__':
    main()
输出
您可以简化代码。如果工作是在子进程中完成的,则不需要多个Python进程。您可以使用
多处理.Pool
来限制并发子进程的数量:

#!/usr/bin/env python
import multiprocessing.dummy as mp # use threads
from random import randrange
from subprocess import check_call
from timeit import default_timer as timer

def info(msg, _print_lock=mp.Lock()): # a poor man's logging.info()
    with _print_lock: # avoid garbled output
        print("%s\t%s" % (mp.current_process().name, msg))

def work(sec):
    try: # wrap in try/except to avoid premature exit
        info("Sleeping for %d seconds" % (sec,))
        start = timer()
        check_call(["sleep", str(sec)])
    except Exception as e: # error
        return sec, timer() - start, e
    else: # success
        return sec, timer() - start, None

def main():
    work_items = (randrange(1, 5) for i in range(20)) # you can use generator
    pool = mp.Pool(5) # pool of worker threads
    for result in pool.imap_unordered(work, work_items):
        info("expected %s, got %s, error %s" % result)
    pool.close()
    pool.join()

if __name__ == '__main__':
    main()
输出
谢谢:)我想如果有4-5个转换,或者只有1个转换,最好是进行一些转换并签入
top
。对于这样的进程和操作系统,我真的是个新手。谢谢:)我想如果有4-5个或者只有1个,最好是进行一些转换,然后签入
top
。说到进程和操作系统之类的东西,我真的是个新手。ps输出只是显示您有一个父根python进程,它派生了4个子进程。这并不意味着一切都在一个进程内运行。显示的树只是父子关系。我不知道是否有一种方法可以在python中手动设置CPU内核之间的关联性,因此,通过这种方式,我们假设操作系统负责内核之间进程的分布。我发现了一个有趣的库,它可以帮助您跟踪进程在内核之间的分布情况。这里是-看看get\u cpu\u affinity()set\u cpu\u affinity()功能。顺便说一句,我仍然坚持这个建议