使用多处理将方法并行应用于对象列表

时间:2017-03-24 14:58:32

标签: python multiprocessing

我创建了一个包含许多方法的类。其中一种方法非常耗时,my_process,并且我希望并行执行该方法。我遇到Python Multiprocessing - apply class method to a list of objects,但我不确定如何将其应用于我的问题,以及它对我班级的其他方法会产生什么影响。

class MyClass():
    def __init__(self, input):
        self.input = input
        self.result = int

    def my_process(self, multiply_by, add_to):
        self.result = self.input * multiply_by
        self._my_sub_process(add_to)
        return self.result

    def _my_sub_process(self, add_to):
        self.result += add_to

list_of_numbers = range(0, 5)
list_of_objects = [MyClass(i) for i in list_of_numbers]
list_of_results = [obj.my_process(100, 1) for obj in list_of_objects] # multi-process this for-loop

print list_of_numbers
print list_of_results

[0, 1, 2, 3, 4]
[1, 101, 201, 301, 401]

5 个答案:

答案 0 :(得分:13)

我会在这里反对谷物,并建议坚持可能有效的最简单的事情;-)即Pool.map() - 类似的功能是理想的,但仅限于通过单一论点。而不是围绕它做出英勇的努力,只需编写一个只需要一个参数的辅助函数:一个元组。然后一切都变得简单明了。

这是一个完整的程序,采用这种方法,在Python 2下打印您想要的内容,无论操作系统如何:

class MyClass():
    def __init__(self, input):
        self.input = input
        self.result = int

    def my_process(self, multiply_by, add_to):
        self.result = self.input * multiply_by
        self._my_sub_process(add_to)
        return self.result

    def _my_sub_process(self, add_to):
        self.result += add_to

import multiprocessing as mp
NUM_CORE = 4  # set to the number of cores you want to use

def worker(arg):
    obj, m, a = arg
    return obj.my_process(m, a)

if __name__ == "__main__":
    list_of_numbers = range(0, 5)
    list_of_objects = [MyClass(i) for i in list_of_numbers]

    pool = mp.Pool(NUM_CORE)
    list_of_results = pool.map(worker, ((obj, 100, 1) for obj in list_of_objects))
    pool.close()
    pool.join()

    print list_of_numbers
    print list_of_results

很大的魔力

我应该注意采取我建议的非常简单的方法有很多好处。除此之外它在Pythons 2和3上“正常工作”,不需要对类进行任何更改,并且易于理解,它也适用于所有Pool方法。

但是,如果你想要并行运行多个方法,那么为每个方法编写一个微小的工作函数会有点烦人。所以这里有一点“神奇”的蠕虫。像这样更改worker()

def worker(arg):
    obj, methname = arg[:2]
    return getattr(obj, methname)(*arg[2:])

现在,单个工作器函数可以满足任意数量的方法,包含任意数量的参数。在您的具体情况下,只需更改一行以匹配:

list_of_results = pool.map(worker, ((obj, "my_process", 100, 1) for obj in list_of_objects))

或多或少明显的概括也可以满足具有关键字参数的方法。但是,在现实生活中,我通常坚持原始建议。在某些方面,迎合概括确实弊大于利。然后,我喜欢显而易见的事情; - )

答案 1 :(得分:2)

通常,并行运行相同计算的最简单方法是map的{​​{1}}方法(或Python 3中multiprocessing.Pool的{​​{1}}函数)。< / p>

但是,as_completed方法使用多个进程将一个函数应用于只有一个参数到一个可迭代的数据。

所以这个函数不能是一个普通的方法,因为它至少需要两个参数;它还必须包括concurrent.futures!然而,它可能是一种静态方法。另请参阅this answer以获得更深入的解释。

答案 2 :(得分:1)

如果你的班级不是“庞大”,我认为面向流程更好。 建议使用多处理池 这是教程 - &gt; https://docs.python.org/2/library/multiprocessing.html#using-a-pool-of-workers

然后将add_tomy_process分开,因为它们很快,您可以在最后一个过程结束时等待util。

def my_process(input, multiby):
    return xxxx
def add_to(result,a_list):
    xxx
p = Pool(5)
res = []
for i in range(10):
    res.append(p.apply_async(my_process, (i,5)))
p.join()  # wait for the end of the last process
for i in range(10):
    print res[i].get()

答案 3 :(得分:1)

根据Python Multiprocessing - apply class method to a list of objects的答案和您的代码:

  1. public int ID { get; set; } [System.ComponentModel.DisplayName("Full_Name:")] public string FullName { get; set; } public string Adress { get; set; } public string SSID { get; set; } 添加到MyClass object

    simulation object
  2. class simulation(multiprocessing.Process): def __init__(self, id, worker, *args, **kwargs): # must call this before anything else multiprocessing.Process.__init__(self) self.id = id self.worker = worker self.args = args self.kwargs = kwargs sys.stdout.write('[%d] created\n' % (self.id)) 功能

    中运行您想要的内容
    run
  3. 试试这个:

        def run(self):
            sys.stdout.write('[%d] running ...  process id: %s\n' % (self.id, os.getpid()))
            self.worker.my_process(*self.args, **self.kwargs)
            sys.stdout.write('[%d] completed\n' % (self.id))
    

答案 4 :(得分:0)

如果您不需要坚持使用Multiprocessing模块,那么, 使用concurrents.futures

可以轻松实现

这里是示例代码:

from concurrent.futures.thread import ThreadPoolExecutor, wait

MAX_WORKERS = 20

class MyClass():
    def __init__(self, input):
        self.input = input
        self.result = int

    def my_process(self, multiply_by, add_to):
        self.result = self.input * multiply_by
        self._my_sub_process(add_to)
        return self.result

    def _my_sub_process(self, add_to):
        self.result += add_to

list_of_numbers = range(0, 5)
list_of_objects = [MyClass(i) for i in list_of_numbers]

With ThreadPoolExecutor(MAX_WORKERS) as executor:
    for obj in list_of_objects:
        executor.submit(obj.my_process, 100, 1).add_done_callback(on_finish)

def on_finish(future):
    result = future.result() # do stuff with your result

这里执行程序返回它提交的每个任务的未来。请记住,如果您使用add_done_callback()完成的任务从线程返回到主线程(这将阻止您的主线程),如果您真的想要真正的并行性,那么您应该等待未来的对象单独。这是代码片段。

futures = []
with ThreadPoolExecutor(MAX_WORKERS) as executor:
    for objin list_of_objects:
        futures.append(executor.submit(obj.my_process, 100, 1))
wait(futures)

for succeded, failed in futures:
    # work with your result here
    if succeded:
       print (succeeeded.result())
    if failed:
        print (failed.result())

希望这会有所帮助。