了解 python 多处理池映射线程安全
Understanding python multiprocessing pool map thread safety
这个问题的答案相互矛盾:Are Python multiprocessing Pool thread safe?
我是并发模式的新手,我正在尝试 运行 一个接受数组并将数组的工作分配到多个进程的项目。数组很大。
inputs = range(100000)
with Pool(2) as pool:
res = pool.map(some_func, inputs)
我的理解是pool会把任务分发给进程。我的问题是:
- 这个地图操作线程安全吗?两个进程是否会不小心尝试处理相同的值?
- 我肤浅地理解任务会被分成块并发送给进程。但是,如果不同的输入比其他的输入花费更多的时间,工作是否总是均匀地分布在我的流程中?我是否会遇到一个进程挂起但在其他进程空闲时有一长串任务要做的情况?
- 我的理解是,因为我只是读取输入,所以我不需要使用任何进程间通信模式,例如服务器管理器/共享内存。是吗?
- 如果我设置的进程多于内核,它基本上会像 CPU 在任务之间切换的线程一样运行吗?
谢谢!
- 使用提供的代码,不可能
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
,则每个块都是 iterable 中的一项(相当于 chunksize=1
)。每个chunk都会'sent'一个接一个到pool中的可用进程。当它们完成前一个工作并变得可用时,这些块将被发送到进程。不需要每个进程都采用相同数量的块。在您的示例中,如果 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)
这个问题的答案相互矛盾:Are Python multiprocessing Pool thread safe?
我是并发模式的新手,我正在尝试 运行 一个接受数组并将数组的工作分配到多个进程的项目。数组很大。
inputs = range(100000)
with Pool(2) as pool:
res = pool.map(some_func, inputs)
我的理解是pool会把任务分发给进程。我的问题是:
- 这个地图操作线程安全吗?两个进程是否会不小心尝试处理相同的值?
- 我肤浅地理解任务会被分成块并发送给进程。但是,如果不同的输入比其他的输入花费更多的时间,工作是否总是均匀地分布在我的流程中?我是否会遇到一个进程挂起但在其他进程空闲时有一长串任务要做的情况?
- 我的理解是,因为我只是读取输入,所以我不需要使用任何进程间通信模式,例如服务器管理器/共享内存。是吗?
- 如果我设置的进程多于内核,它基本上会像 CPU 在任务之间切换的线程一样运行吗?
谢谢!
- 使用提供的代码,不可能
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
,则每个块都是 iterable 中的一项(相当于chunksize=1
)。每个chunk都会'sent'一个接一个到pool中的可用进程。当它们完成前一个工作并变得可用时,这些块将被发送到进程。不需要每个进程都采用相同数量的块。在您的示例中,如果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)