参考链接:
multiprocessing官网
https://blog.csdn.net/cityzenoldwang/article/details/78584175 博主整理
https://blog.csdn.net/quqiuzhu/article/details/51156454博主整理
Process类
Process 类用来描述一个进程对象。创建子进程的时候,只需要传入一个执行函数和函数的参数即可完成 Process 示例的创建。
- star() 方法启动进程,
- join() 方法实现进程间的同步,等待所有进程退出才执行下面的代码
- close() 用来阻止多余的进程涌入进程池 Pool 造成进程阻塞。
multiprocessing.Process(group=None, target=None, name=None,args=(), kwargs={},daemon=None) - target 是函数名字,需要调用的函数
- args 函数需要的参数,以 tuple 的形式传入,当传入一个参数是,要加,号,因为(1)不是一个tuple,而(1,)才是一个tuple
给函数分配进程运行实例,创建单进程:
import multiprocessing as mp
def job(a, b):
print(a+b)
if __name__ == '__main__': # 必须将进程过程放到main函数中去
p1 = mp.Process(target=job, args=(1, 2))
p1.start()
p1.join()
创建多进程:
import multiprocessing
import os
def run_proc(name):
print('Child process {0} {1} Running '.format(name, os.getpid()))
if __name__ == '__main__':
print('Parent process {0} is Running'.format(os.getpid()))
for i in range(5):
p = multiprocessing.Process(target=run_proc, args=(str(i),))
print('process start')
p.start()
p.join()
print('Process close')
运行结果:
Parent process 6296 is Running
process start
process start
process start
process start
process start
Child process 0 9428 Running
Child process 1 8444 Running
Child process 2 7852 Running
Child process 3 6540 Running
Child process 4 14472 Running
Process close
如果将p.join去掉,结果为:
Parent process 8712 is Running
process start
process start
process start
process start
process start
Child process 0 10516 Running
Process close
Child process 1 3172 Running
Child process 2 10748 Running
Child process 3 11636 Running
Child process 4 5484 Running
Queue
使用Queue存储进程输出,用于多个进程间的通信
Queue的功能是将每个核或线程的运算结果放在队里中, 等到每个线程或核运行完毕后再从队列中取出结果, 继续加载运算。原因很简单, 多进程调用的函数不能有返回值(不能return), 所以使用Queue存储多个进程运算的结果。
put方法:插入数据到队列。
get方法:从队列中读取并删除一个元素。
import multiprocessing as mp
def job(q):
for i in range(10):
q.put(i) # 存放到队列中
if __name__ == '__main__':
q = mp.Queue() # 创建队列
# args里一个参数时要加,号,否则会报错参数是不可迭代的
p1 = mp.Process(target=job, args=(q,))
p2 = mp.Process(target=job, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
res1 = q.get() # 获取队列中的值
res2 = q.get()
res3 = q.get()
print(res1) # 0
print(res2) # 1
print(res3) # 2
Pool进程池
进程池就是我们将所要运行的东西,放到进程池中,Python会自行解决多进程的问题。Pool默认大小是CPU的核数,我们也可以通过在Pool中传入processes参数自定义需要的核数量。定义进程池之后,就可以让进程池对应某一个函数,通过向进程池中传入数据从而返回函数值。 Pool和之前的Process的不同点是传入Pool的函数有返回值,而Process的没有返回值。
map方法:用map()获取结果,在map()中需要放入函数和需要迭代运算的值,然后它会自动分配给CPU核,返回结果。
apply_async方法:apply_async()中只能传递一个值,它只会放入一个核进行运算,但是传入值时要注意是元组类型,所以在传入值后需要加逗号, 同时需要用get()方法获取返回值。如果要实现map()的效果,需要将apply_async方法做成一个列表的形式。
进程池最后要加join方法,这样进程池运行完毕后才向下进行,如果不加的话可能导致进程池还未运行完程序已经finished。
代码如下。
import multiprocessing as mp
def job(x):
return x * x
def multicore():
pool = mp.Pool(processes=mp.cpu_count() - 1)
res = pool.map(job, range(10))
print(res)
res = pool.apply_async(job, (2,))
print(res.get())
multi_res = [pool.apply_async(job, (i,)) for i in range(10)]
pool.close() # 关闭进程池,其他进程无法加入
pool.join() # 等待所有进程执行完毕,调用前必须调用close方法
print([res.get() for res in multi_res])
if __name__ == '__main__':
multicore()
运行结果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
4
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
多进程(multiprocessing)和多线程(multi-threading)对比:
测试程序:
import multiprocessing as mp
import threading as td
import time
MAX = 10000000
def job(q):
res = 0
for i in range(MAX):
res += i+i**2+i**3
q.put(res)
def multicore():
q = mp.Queue()
p1 = mp.Process(target=job, args=(q,))
p2 = mp.Process(target=job, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
res1 = q.get()
res2 = q.get()
print('multicore:', res1+res2)
def normal():
res = 0
for _ in range(2):
for i in range(MAX):
res += i+i**2+i**3
print('normal:', res)
def multithread():
q = mp.Queue()
t1 = td.Thread(target=job, args=(q,))
t2 = td.Thread(target=job, args=(q,))
t1.start()
t2.start()
t1.join()
t2.join()
res1 = q.get()
res2 = q.get()
print('multithreading:', res1+res2)
if __name__ == '__main__':
st = time.time()
normal()
st1 = time.time()
print('normal time:', st1 - st)
multithread()
st2 = time.time()
print('multithreading time:', st2 - st1)
multicore()
print('multicore time:', time.time()-st2)
运行结果:
normal time: 23.027679920196533
multithreading: 4999999666666716666660000000
multithreading time: 24.3942768573761
multicore: 4999999666666716666660000000
multicore time: 19.363178968429565
从上述结果来看,多进程的时间是要小于多线程和正常程序的,多线程的时间与正常时间相差无几。原因是Python解释器有一个全局解释器锁(GIL),导致每个Python进程最多同时运行一个线程,因此Python多线程程序并不能改善程序性能,不能发挥CPU多核的优势,通过GIL这篇文章可以了解。但是多进程程序可以不受影响,Python2.6引入multiprocessing来解决多进程问题,而multiprocessing的API几乎是复制了threading的API。