【问题标题】:Understanding python multiprocessing pool map thread safety了解python多处理池映射线程安全
【发布时间】:2021-12-27 03:53:33
【问题描述】:
这个问题的答案相互矛盾:Are Python multiprocessing Pool thread safe?
我是并发模式的新手,我正在尝试运行一个接收数组并将数组的工作分配到多个进程的项目。数组很大。
inputs = range(100000)
with Pool(2) as pool:
res = pool.map(some_func, inputs)
我的理解是池将任务分配给进程。我的问题是:
- 这个地图操作线程安全吗?两个进程是否会意外尝试处理相同的值?
- 我表面上理解任务将被分成块并发送到进程。但是,如果不同的输入比其他输入花费更多的时间,那么工作是否会始终均匀地分布在我的流程中?我是否会遇到这样一种情况:一个进程挂起,但有很长的任务队列要做,而其他进程处于空闲状态?
- 我的理解是,由于我只是在读取输入,因此我不需要使用任何进程间通信模式,例如服务器管理器/共享内存。对吗?
- 如果我设置的进程多于内核,它基本上会像 CPU 在任务之间切换的线程一样运行吗?
谢谢!
【问题讨论】:
标签:
python
multithreading
concurrency
thread-safety
python-multiprocessing
【解决方案1】:
- 使用提供的代码,
inputs 的同一项不可能被多个进程处理(如果对象的同一实例在作为参数传递的迭代中出现多次则例外) .然而,这种使用多处理的方式有很多开销,因为inputs 项目被一个接一个地发送到进程。更好的方法是使用chunksize 参数:
inputs = range(100000)
n_proc = 2
chunksize = len(inputs)//n_proc
if len(inputs) % n_proc:
chunksize += 1
with Pool(nproc) as pool:
res = pool.map(some_func, inputs, chunksize=chunksize)
这样,inputs 的块会立即传递给每个进程,从而获得更好的性能。
-
除非您要求,否则工作不会被分成几块。如果没有提供chunksize,则每个块都是可迭代对象中的一项(相当于chunksize=1)。每个块将被一个一个地“发送”到池中的可用进程。这些块在完成前一个进程并变得可用时被发送到进程。不需要每个进程都采用相同数量的块。在您的示例中,如果some_func 较大的值需要更长的时间,而chunksize = len(items)/2 获取具有inputs 前半部分(具有较小值)的块的过程将首先完成,而另一个需要更长的时间。在这种情况下,较小的块是更好的选择,这样工作会均匀分布。
-
这取决于some_func 的作用。如果不需要some_func(n) 的结果来处理some_func(m),则不需要进程间通信。如果您正在使用map并且需要在进程之间进行通信,那么很可能您在解决问题时采用了不好的方法。
-
如果max_workers > os.cpu_count() CPU 将在进程之间切换比进程数较少时更频繁。不要忘记,在(不是很老的)计算机上运行的进程比您的程序多得多。在 Windows 中,max_workers 必须等于或小于 61(请参阅文档 here)