python多进程模块(multiprocessing)的基本使用方法(想要轻松上手的不要错过啦!)
python语言中默认含有的全局解释器锁(即GIL),由于全局解释器锁的存在,每个运行任务只能在获取全局解释器锁之后才能开始运行,使用多线程时某线程在执行100条字节码后,当前线程就自动释放GIL锁,让别的线程有机会执行。因此即使使用多线程,也是不能真正达到并行的。正因为这样的限制使得Python多线程有时候显得很无用。当然在某些I/O密集型任务中还是可以使用多线程方法的,因为在等待时间其余线程可以继续执行任务。
在需要利用多核CPU完成某些计算密集型任务时,就需要用到多进程方法了,python中的多进程大概有os.fork()、multiprocessing、concurrent.future()模块。其中os.fork()模块的使用场景较少,并且只能运行于类unix系统上,另外两个则可移植性更强,使用也较为方便,本文主要介绍multiprocessing模块的几种使用方式。
调用multiprocessing模块创建多进程任务一般有两个方法即Process(创建进程)与Pool(创建进程池)。
需要注意多进程产生的进程都是有进程号的,是能够实时监控到的。多进程也是一般用于函数环境的!
Process类
# 创建一个进程(仅介绍,创建一个进程没啥意义嘛,哈哈!)
import multiprocessing
def test(i):
print(i)
if __name__ == '__main__':
process1 = multiprocessing.Process(target=test,args=('jack',))
# 这就是开启进程的方法,target为要运行的函数,args为需要向将要运行的函数传入的参数。
# 单个参数后加上,(逗号)表示传入的参数是可以迭代的,单个参数时必须加上,
process1.start() # 开始刚刚创建的新进程
process1.join()
'''
join是用于等待所有子进程结束后才结束主进程的方法,若有疑问可查看我之前的介绍join方法的文章
https://blog.csdn.net/keepaware/article/details/108432899
'''
# 创建多个进程
import multiprocessing
def test(i):
print(i)
if __name__ == '__main__':
record = [] # 创建一个record列表用于储存产生的进程,利于后续的join操作
for p in range(4): # 使用for循环用于后续创建四个进程
process = multiprocessing.Process(target=test,args=(p,))
'''
这里要注意,如果每次的参数都输入一样的,这样每个进程都会运行一样的任务,也就是说每个进程都是从
头开始运行。这就可能会导致结果不尽人意,不要问我为啥知道,呜呜!因此需要自己设计方法来避免这个
坑,例如制作一个参数列表,从列表中提取每个参数。后续还有map方法也可匹配列表
'''
process.start()
# 开始创建的新进程,此处不调用join是因为调用后主进程会等待每个子进程结束后才开始下个子进程
record.append(process) # 将每个创建的进程都放入record列表中
for process in record:
process.join()
# 从record列表中提取每个子进程使用join方法,此时子进程都已经在运行了,再等待每个子进程都结束即可
Pool类(进程池)
Pool.map()
# 进程池即创建一个进程库,事先设置好需要多少进程,从进程库中提取这些已创建的进程即可。
# Pool.map()
import multiprocessing
def test2(i):
print(i)
if __name__ == '__main__':
list1 = list(range(5)) # 创建一个列表,map方法会用到
pool1 = multiprocessing.Pool(processes=4) # 创建一个进程池,这里创建了含有4个进程的进程池
pool1.map(test2,list1)
'''
对pool1使用map方法,在这个例子中即形成(test2(0),test2(1),test2(2),test2(3),test2(4) 这些任务)
这些将执行的任务是按顺序执行的,也就是说进程1执行test2(0),进程2执行test2(1),进程3执行test2(2),
进程4执行test2(3),还多出一个任务则需要等待,等到某个进程提前结束了就再执行这个任务。
这样的话相当于一个进程执行一次单独的任务,十分方便某些任务
如果函数有返回值即return,还可以使用result = pool1.map(test2,list1)获取每个进程的返回值,
但是这里是所有返回值在一起,需要自己对其按需处理。
'''
pool1.close() # close方法用于关闭进程池,即恢复到没有子进程的情况
pool1.join()
Pool.apply_async()
除了apply_async外还有apply,不过apply已经近乎被淘汰了,因为apply是阻塞的,需要一个进程结束才能开始下个进程。此方法使用方式类似于process方法,是一种异步非阻塞执行多进程的方式,但是需要在进程池的基础下进行
# 使用Pool.apply_async() 创建一个进程
import multiprocessing
def test3(i):
print(i)
if __name__ == '__main__':
with multiprocessing.Pool(processes=4) as pool: # 使用with方法不用后续关闭Pool
result = pool.apply_async(test3,args=('jack',)) # 此处同上,参数不变的话执行的是同一任务
res = result.get() # get方法可以获取进程的返回值,此处可以使用res进行后续操作
# 创建多个进程
def test4(i):
print(i)
if __name__ == '__main__':
res_list = []
with multiprocessing.Pool(processes=4) as pool:
for p in range(10):
ret = pool.apply_async(test4,args=(p,))
ret_list.append(ret)
for ret in ret_list:
ret.get()
若是想对不同的任务进行多进程操作,也是相当简单的,因为实际上每个多进程操作结束后都是会关闭或者join的,因此在每个多进程操作结束后再开启新的多进程任务即可
例:
import multiprocessing
def test5(i):
print(i)
if __name__ == '__main__':
list1 = list(range(10))
res_list = []
for item in list1:
with multiprocessing.Pool(processes=4) as pool:
for p in range(item):
ret = pool.apply_async(test5,args=(p,))
ret_list.append(ret)
for ret in ret_list:
ret.get()
上一篇: 机器视觉——旋转矩阵的计算(二)
下一篇: vuex 快速上手具体使用方法总结