什么是Multiprocessing?
大部分计算机cpu都是多核的,为了提高效率,把程序分配到多个核里面同时运行,这就叫多进程。
Python提供了一个mulitprocessing 库来实现多进程
(本文是学习“莫烦Python后写的总结,分析与感悟。这里是莫烦python的连接: >点这里< )
1:基本操作:创建进程
a. 创建一个函数,且不能有返回值
b. 创建子进程对象,函数名传递给 target,参数放在一个可迭代对象内传递给 args,(注意,若只有一个元素1,且是放在小括号内,则应该写为:args = (1,) 因为加个逗号才算是元组,才可迭代)
c. 运行子进程(注意!!需要在if __name__ == '__main__':下运行)
'''创建进程'''
import multiprocessing as mp
def job_1(a, b):
for _ in range(5):
print("job_1 is running")
if __name__ == '__main__':
print("main start")
#创建子进程
p1 = mp.Process(target=job_1, args=(1, 2))
#子进程开始运行
p1.start()
print("main end")
这个时候,main相当于主进程,p1是他的子进程,一旦运行到p1.start(),job1才开始运行。而main 和 p1各自运行各自的,并不会相互等待,所以就出现了以下结果:
2.等待主进程:join()
join()的官方解释:阻塞当前进程,直到调用join方法的那个进程执行完。
意思就是:等当前子进程(p1 或 p2) 运行完毕之后,主进程(main)才继续运行。(注意:如果两个子进程都使用join,子进程之间不会互相等待,仍然是各走各的)
# 只需要把join放在主进程需要等待的地方即可
准备工作:
import multiprocessing as mp
import time
def run_1():
for _ in range(5):
time.sleep(0.1)
print("job_1 is runing...")
def run_2():
for _ in range(5):
time.sleep(0.1)
print("job_2 is runing...")
if __name__ == '__main__':
print("main start")
p1 = mp.Process(target=run_1)
p2 = mp.Process(target=run_2)
情况1:main, p1, p2 三个进程互不干扰
p1.start()
p2.start()
print("main end")
结果:各自运行,非常混乱
情况2:p1, p2都调用join()
p1.start()
p2.start()
p1.join()
p2.join()
print("main end")
结果:主程序等待p1, p2运行完了之后才运行,但是p1, p2依旧混乱
情况3:p1调用join()之后再调用p2
p1.start()
p1.join()
p2.start()
print("main end")
结果有些许奇怪,这是为什么呢?因为p1 调用join之后,main程序在等待p1运行完的时候,根本还没开始运行代码 p2.start() !
"
3.特殊的返回值的方法:Queue
多进程中,可以利用队列来存储返回值,在进程结束之后再调取存储的返回值
a.声明一个queue,并把queue作为参数传入方法中
b.待进程结束后提取 “返回值”
import multiprocessing as mp
import time
def job_1(q, a, b):
sum = a * b
q.put(sum)
def job_2(q, a, b):
sum = a / b
q.put(sum)
if __name__ == '__main__':
q = mp.Queue()
p1 = mp.Process(target=job_1, args=(q, 1, 2))
p2 = mp.Process(target=job_2, args=(q, 1, 2))
p1.start()
p2.start()
p1.join()
p2.join()
print(q.get(), q.get())
4.一个池子:pool
pool也叫做进程池,就是我们将所要运行的东西,放到池子里,Python会自行解决多进程的问题
用法0:自定义核的数量:pool = mp.Pool(processes = 2) 即pool对象运行时只使用两个核
用法1:给pool对象一个函数,并传入一个迭代对象(迭代的是传入的参数),pool会返回一个返回值列表
import multiprocessing as mp
def job(a):
return a*a
if __name__ == '__main__':
pool = mp.Pool(processes = 2)
returns = pool.map(job, range(10))
print(returns)
用法2:apply_async()方法,这个方法类似于直接调用start(),但它接受了一个返回值,而且一个方法只使用一个核。
if __name__ == '__main__':
pool = mp.Pool()
returns = pool.apply_async(job, (3,))
print(returns.get())
5.共同处理数据:共享内存
如果不用共享变量,传入子程序的参数只是一个副本,并不能改变传入的变量的值,也不能相互交流
所以在多核处理的特殊情况下,我们需要用到共享内存处理、需要子进程之间相互交流数据的情况
操作1:定义单个共享变量:v = mp.Value('i', 0)
这里我们呢在两个子程序里面分别对变量加 5 次 2,和 5 次 4 预想的最终结果是 30
import multiprocessing as mp
def run_1(a):
for _ in range(5):
a.value = a.value + 2
print(a.value)
def run_2(a):
for _ in range(5):
a.value = a.value + 4
print(a.value)
if __name__ == '__main__':
v = mp.Value('i', 0)
p1 = mp.Process(target=run_1, args=(v,))
p2 = mp.Process(target=run_2, args=(v,))
p1.start()
p2.start()
p1.join()
p2.join()
print(v.value)
输出:发现数据实现了共享,且最后也确实被改变成了30。
2
8
10
12
14
6
18
22
26
30
30
操作2:定义变量数组:(注意!!这里只能定义一维数组)
array = mp.Array('i', [1, 2, 3, 4])
6.维持秩序:lock
这里回去第 5 节的输出结果,会发现共享的变量被两个进程抢着用,一会儿加2, 一会儿加4, 一会儿你print,一会儿我print
有时候我们需要两个程序有顺序的完成各自的任务,不能让他们抢夺变量,这是后就可以用到 进程锁
import multiprocessing as mp
def run_1(a, L):
#锁住
L.acquire()
for _ in range(5):
a.value = a.value + 2
print(a.value)
L.release()
#释放
def run_2(a, L):
L.acquire()
for _ in range(5):
a.value = a.value + 4
print(a.value)
L.release()
if __name__ == '__main__':
v = mp.Value('i', 0)
#创建进程索
L = mp.Lock()
p1 = mp.Process(target=run_1, args=(v,L))
p2 = mp.Process(target=run_2, args=(v,L))
p1.start()
p2.start()
p1.join()
p2.join()
print(v.value)
这样一来,输出就变得有序起来了:
2
4
6
8
10
14
18
22
26
30
30