多重处理:如何mp.map一个将元素存储在列表中的函数?

时间:2019-05-04 12:38:29

标签: python parallel-processing multiprocessing python-multiprocessing

我有一个类似于以下程序:

import time
from multiprocessing import Pool

class a_system():
    def __init__(self,N):
        self.N = N
        self.L = [0 for _ in range(self.N)]
    def comp(self,n):
        self.L[n] = 1
        return self.L
    def reset(self):
        self.L = [0 for _ in range(self.N)]

def individual_sim(iter):
    global B, L, sys
    sys.reset()
    L[iter] = sys.comp(iter)
    B += sum(L[iter])
    time.sleep(1)
    return L, B

def simulate(N_mc):
    global B, L, sys
    L = [[] for _ in range(N_mc)]
    B = 0
    sys = a_system(N_mc)
    [*map(individual_sim, range(N_mc))]
    # with Pool() as P:
    #     P.map(individual_sim,range(N_mc))
    return L, B

if __name__=="__main__":
    start = time.time()
    L, B = simulate(N_mc=5)
    print(L)
    print(B)
    print("Time elapsed: ",time.time()-start)

在这里,我想将行[*map(individual_sim, range(N_mc))]与多处理并行化。但是,将这一行替换为

with Pool() as P:
     P.map(individual_sim,range(N_mc))

返回一个空的列表列表。

如果我改用P.map_asyncP.imapP.imap_unordered,则不会出错,但列表和B留空。

如何并行处理此代码?

P.S。 我尝试了ThreadPool中的multiprocessing.pool,但是我想避免这种情况,因为类a_system(这里显示的类要复杂一点)需要使用其他副本每个工人(我得到一个exit code 139 (interrupted by signal 11: SIGSEGV))。

P.S.2 我可能会尝试使用sharedctypes或Managers(?),但是我不确定它们如何工作,也不确定应该使用哪一个(或组合使用)。

P.S.3 我也尝试将individual_sim修改为

def individual_sim(iter,B,L,sys):
    sys.reset()
    L[iter] = sys.comp(iter)
    B += sum(L[iter])
    time.sleep(1)
    return L, B

,并在simulation中使用以下内容:

   from functools import partial
   part_individual_sim = partial(individual_sim, B=B, L=L, sys=sys)
   with Pool() as P:
        P.map(part_individual_sim,range(N_mc))

但我仍然得到空名单。

2 个答案:

答案 0 :(得分:3)

我不清楚您的业务逻辑在哪里,但是您不能在子流程中修改父级的全局变量。单独的进程不共享其地址空间。

不过,您可以将cat ~/.docker/config.json | jq -r ".auths[\"https://${REGISTRY}\"].auth" | base64 -d)"设为L,将Manager.List设为B,以从工作进程中进行修改。管理器对象位于单独的服务器进程中,您可以使用代理对象对其进行修改。此外,在修改这些共享对象时,您需要使用Manager.Value,以防止数据损坏。

这是一个精简的示例,可以帮助您入门:

Manager.Lock

示例输出:

import time
from multiprocessing import Pool, Manager


def individual_sim(mlist, mvalue, mlock, idx):
    # in your real computation, make sure to not hold the lock longer than
    # really needed (e.g. calculations without holding lock)
    with mlock:
        mlist[idx] += 10
        mvalue.value += sum(mlist)


def simulate(n_workers, n):

    with Manager() as m:
        mlist = m.list([i for i in range(n)])
        print(mlist)
        mvalue = m.Value('i', 0)
        mlock = m.Lock()

        iterable = [(mlist, mvalue, mlock, i) for i in range(n)]

        with Pool(processes=n_workers) as pool:
             pool.starmap(individual_sim, iterable)

        # convert to non-shared objects before terminating manager
        mlist = list(mlist)
        mvalue = mvalue.value

    return mlist, mvalue


if __name__=="__main__":

    N_WORKERS = 4
    N = 20

    start = time.perf_counter()
    L, B = simulate(N_WORKERS, N)
    print(L)
    print(B)
    print("Time elapsed: ",time.perf_counter() - start)

还可以使用Pool的[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] 5900 Time elapsed: 0.14064819699706277 Process finished with exit code 0 参数在工作人员初始化时传递代理并将其注册为全局变量,而不是通过starmap-call将它们作为常规参数发送。

我已经写了simple list operation。有关initializer使用的更多信息(相关:嵌套代理)。

答案 1 :(得分:1)

multiprocessing模块的工作方式是fork设置主进程(或执行更多的Python解释器副本,尤其是在Windows下)。

因此,您将看到全局变量,但是它们不会在进程之间共享-除非您采用特殊措施,例如显式共享内存。您最好不要将所需的状态作为函数参数(或通过Pool的{​​{1}}和initializer)作为参数传递,并将结果通过返回值传递回去。

这往往会限制您的设计选择,特别是如果您需要传递很多状态(例如,作为您要容纳的数据)

这是一个非常轻量级的包装器,用于包装相当底层的原语,因此它不像initargs这样的功能,但是如果可以忍受约束的话,性能往往会更好

编辑以包含一些演示代码,这些演示代码假定您的问题中的Dask变量与您执行一些Monte Carlo /随机近似有关。我首先提取一些库:

N_mc

并定义一个工作函数和代码进行初始化:

from multiprocessing import Pool

from PIL import Image
import numpy as np

def initfn(path): # make sure worker processes don't share RNG state, see: # https://github.com/numpy/numpy/issues/9650 np.random.seed() global image with Image.open(path) as img: image = np.asarray(img.convert('L')) def worker(i, nsamps): height, width = image.shape subset = image[ np.random.randint(height, size=nsamps), np.random.randint(width, size=nsamps), ] return np.mean(subset) def mc_mean(path, nsamples, niter): with Pool(initializer=initfn, initargs=(path,)) as pool: params = [(i, nsamples) for i in range(niter)] return pool.starmap(worker, params) 将JPEG / PNG文件读取到一个numpy数组中,然后initfn仅计算一些随机像素子集的平均值(即亮度)。请注意,彩色图像被加载为3d矩阵,并以worker进行索引(通道通常为0 =红色,1 =蓝色,2 =绿色)。另外,我们还显式调用[row, col, channel],以确保我们的工作作业不会获得相同的随机值序列。

然后我们可以运行它并绘制输出以确保一切正常:

np.random.seed

应该给我们这样的东西

MC distribution

显然,这取决于所使用的图像,该图像来自this JPEG