python语法——使用Pool实现多进程并行
简介
可以使用 Pool来实现多进程并行。
Pool 模块来自于 multiprocessing 模块。
multiprocessing 模块是跨平台版本的多进程模块,像线程一样管理进程,与 threading 很相似,对多核CPU的利用率会比 threading 好的多。
Pool 类可以提供指定数量的进程供用户调用,当有新的请求提交到Pool中时,如果池还没有满,就会创建一个新的进程来执行请求。如果池满,请求就会告知先等待,直到池中有进程结束,才会创建新的进程来执行这些请求。
函数
apply()
函数原型:apply(func[, args=()[, kwds={}]])
该函数用于传递不定参数,同python中的apply函数一致,主进程会被阻塞直到函数执行结束(不建议使用,并且3.x以后不在出现)。
apply_async()
函数原型:apply_async(func[, args=()[, kwds={}[, callback=None]]])
与apply用法一致,但它是非阻塞的且支持结果返回后进行回调。
map()
函数原型:map(func, iterable[, chunksize=None])
Pool类中的map方法,与内置的map函数用法行为基本一致,它会使进程阻塞直到结果返回。
map_async()
函数原型:map_async(func, iterable[, chunksize[, callback]])
与map用法一致,但是它是非阻塞的。其有关事项见apply_async。
阻塞与非阻塞的讲解见下面备注。
close()
关闭进程池(pool),使其不在接受新的任务。
terminal()
结束工作进程,不在处理未处理的任务。
join()
主进程阻塞等待子进程的退出, join方法要在close或terminate之后使用。
示例
比如我想同时让服务器执行多条 hive 命令,可编程如下:
from multiprocessing import Pool
import subprocess
# 定义所有并行语句都回调用的函数
def run_sh(sh):
'''
执行一行shell命令
'''
(statusLoad, outputLoad) = subprocess.getstatusoutput(sh)
return (statusLoad, outputLoad)
# 将需要执行的多条语句放入到一个list中
sh_list = []
sh_list.append('hive -e "select * from A" > A_result')
sh_list.append('hive -e "select * from B" > B_result')
sh_list.append('hive -e "select * from C" > C_result')
# 开始并行
pool = Pool(len(sh_list))
pool.map(run_sh, sh_list) # 表示将 sh_list 每个元素作为参数递给 run_sh
pool.close() # 将进程池关闭,不再接受新的进程
pool.join() # 主进程阻塞,只有池中所有进程都完毕了才会通过
# 开始处理结果文件,此时三个 *_result 文件肯定是存在并且已经写入完毕的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
备注
1、阻塞与非阻塞的区别
map() 会使进程阻塞,即通过 map() 开启的多进程都结束之后,这个函数才会有返回结果,否则主进程会一直等待,不会往下进行 。
map_async() 为非阻塞,即通过 map_async() 开启多进程之后,立刻会返回结果,主进程会继续往下执行。
注意:
如果后面调用了 join() 函数,则不管之前用的是 map 还是 map_async,主进程都会等待,直到进程池中所有进程执行完毕,才会继续往下执行。
2、starmap 函数
Pool 类中,python 3.X 还引入了 starmap 函数,与 map 的区别在于, starmap 支持将多个参数放入到队列中,不同参数按照顺序以元组形式存放,举例如下:
from multiprocessing import Pool
def func(a, b):
print(a + b)
if __name__=="__main__":
args = [(1,2),(3,4),(5,6)]
pool = Pool(3)
pool.starmap(func, args)
1
2
3
4
5
6
7
8
输出
3
7
11
1
2
3
内存共享问题
多进程并行有一个特点:多个进程之间并不能共享内存。
比如一个人写出了以下代码,期望可以对同一个数进行累加:
from multiprocessing import Pool
def func(dic, c):
dic['count'] += c
if __name__=="__main__":
d = dict()
d['count'] = 0
args = [(d, 1), (d, 2), (d, 3)]
pool = Pool(3)
pool.starmap(func, args)
pool.close()
pool.join()
print(f'dic={d}')
1
2
3
4
5
6
7
8
9
10
11
12
13
但是输出为:
dic={'count': 0}
不是我们想要的结果。
这是因为,多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改。
解决办法
可以使用 multiprocessing.Manager 来创建对象,这样的对象可以被共享,如:
from multiprocessing import Pool, Manager
def func(dic, c):
dic['count'] += c
if __name__=="__main__":
d = Manager().dict() #生成一个字典,可以在多个进程中传递和共享。
d['count'] = 0
args = [(d, 1), (d, 2), (d, 3)]
pool = Pool(3)
pool.starmap(func, args)
pool.close()
pool.join()
print(f'dic={d}')
1
2
3
4
5
6
7
8
9
10
11
12
13
输出是我们所期望的:
dic={'count': 6}
1
Manager() 内部有加锁机制,不允许两个进程同时修改一份数据,因为进程的数据是独立的,因此数据是安全的。
另外,如果只要求并行,不要求必须是多进程,可以使用多线程来实现共享数据。 参照python技巧——使用threadpool实现多线程并行。
---------------------
作者:HappyRocking
来源:CSDN
原文:https://blog.csdn.net/HappyRocking/article/details/83856489
版权声明:本文为博主原创文章,转载请附上博文链接!