我有一个类似于以下程序:
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_async
,P.imap
或P.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))
但我仍然得到空名单。
答案 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
应该给我们这样的东西
显然,这取决于所使用的图像,该图像来自this JPEG。