多进程 Multiprocessing 和多线程 threading 类似, 他们都是在 python 中用来并行运算的. 不过既然有了 threading, 为什么 Python 还要出一个 multiprocessing 呢?
原因很简单, 就是用来弥补 threading 的一些劣势, 即threading的GIL机制无法让thread对于CPU密集型计算没有提速的效果。
1. 使用方法:
(1)和thread比较类似,添加一个进程的方法是:
import multiprocessing as mp
def job(a,d):
print('aaaaa')
if __name__ == '__main__':
#传入1,2
p1 = mp.Process(target=job,args=(1,2))
p1.start()
p1.join()
最终输出结果:
aaaaa
(2)存储进程输出Queue
Queue的功能是将每个核或线程的运算结果放在队里中, 等到每个线程或核运行完毕后再从队列中取出结果, 继续加载运算。原因很简单, 多线程或者多进程调用的函数不能有返回值, 所以使用Queue存储多个线程运算的结果。
import multiprocessing as mp
def job(q):
res = 0
for i in range(1000):
res+=i+i**2+i**3
q.put(res)
if __name__ == '__main__':
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(res1)
print(res2)
print(res1+res2)
(3)对比多线程、多进程、普通的运算时间
import multiprocessing as mp
import threading as td
import time
def job(q):
res = 0
for i in range(100000):
res = i+i**2+i**3
q.put(res) # queue
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 multitd():
q = mp.Queue()
p1 = td.Thread(target=job,args=(q,))
p2 = td.Thread(target=job,args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
res1 = q.get()
res2 = q.get()
print('multitd:',res1+res2)
def normal():
res = 0
for _ in range(2):
for i in range(100000):
res = i+i**2+i**3
print('normal:',res)
if __name__ == '__name__':
st = time.time()
normal()
st1 = time.time()
print('The time consumption(normal):',st1-st)
multitd()
st2 = time.time()
print('The time consumption(multitd):',st2-st1)
multicore()
st3 = time.time()
print('The time consumption(multicore):',st3-st2)
显示结果
"""
# range(1000000)
('normal:', 499999666667166666000000L)
('normal time:', 1.1306169033050537)
('thread:', 499999666667166666000000L)
('multithread time:', 1.3054230213165283)
('multicore:', 499999666667166666000000L)
('multicore time:', 0.646507978439331)
"""
可看出,multicore的速度>普通>multitd的速度。
(4)进程池pool
Pool
默认调用是CPU的核数,传入processes
参数可自定义CPU核数
map()
放入迭代参数,返回多个结果
import multiprocessing as mp
def job(x):
return x*x
def multicore():
#定义一个pool
pool = mp.Pool()
res = pool.map(job,range(10))
print(res)
if __name__ == '__main__':
multicore()
返回结果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[Finished in 0.6s]
我们向池子里丢数据,池子就会返回函数返回的值。 Pool
和之前的Process
的不同点是丢向Pool
的函数有返回值,而Process
的没有返回值。
用map()
获取结果,在map()
中需要放入函数和需要迭代运算的值,然后它会自动分配给CPU核,返回结果。
3.自定义核数量
自定义核数量为3
def multicore():
pool = mp.Pool(processes=3) # 定义CPU核数量为3
res = pool.map(job, range(10))
print(res)
4.apply_async()
只能放入一组参数,并返回一个结果,如果想得到map()
的效果需要通过迭代
#应用apply_async()输出多个结果
multi_res = [pool.apply_async(job,(i,)) for i in range(10)]
#取出参数
print([res.get() for res in multi_res]
结果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[Finished in 0.9s]
(5)共享内存
只有用共享内存才能让CPU之间有交流。
通过Value数据存储在一个共享的内存表中。
import multiprocessing as mp
value1 = mp.Value('i', 0)
value2 = mp.Value('d', 3.14)
其中:“i”是int型(带符号整型);"d"是双精浮点型(double)
各参数代表的数据类型
(6)进程锁
没有进程锁
import multiprocessing as mp
import time
# 我们定义了一个共享变量v,两个进程都可以对它进行操作。
# 在job()中我们想让v每隔0.1秒输出一次累加num的结果,
# 但是在两个进程p1和p2 中设定了不同的累加值。所以接
# 下来让我们来看下这两个进程是否会出现冲突。
def job(v,num):
for _ in range(5):
time.sleep(0.1) #暂停0.1s
v.value += num #v.value获取共享变量值
print(v.value)
def multicore():
v = mp.Value('i',0) #定义共享变量
#一个迭代值是1,一个迭代值是3
p1 = mp.Process(target=job,args=(v,1))
p2 = mp.Process(target=job,args=(v,3))
#不加进程锁,看如何抢资源
p1.start()
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
multicore()
在上面的代码中,我们定义了一个共享变量v
,两个进程都可以对它进行操作。 在job()
中我们想让v
每隔0.1秒输出一次累加num
的结果,但是在两个进程p1
和p2
中设定了不同的累加值。所以接下来让我们来看下这两个进程是否会出现冲突。
1
5
9
13
17
4
8
12
16
20
很明显,出现了进程冲突。
为了解决上述不同进程抢共享资源的问题,我们可以用加进程锁来解决。
import multiprocessing as mp
import time
# 我们定义了一个共享变量v,两个进程都可以对它进行操作。
# 在job()中我们想让v每隔0.1秒输出一次累加num的结果,
# 但是在两个进程p1和p2 中设定了不同的累加值。所以接
# 下来让我们来看下这两个进程是否会出现冲突。
#加入进程锁
def job(v,num,l):
l.acquire() # 锁住
for _ in range(5):
time.sleep(0.1) #暂停0.1s
v.value += num #v.value获取共享变量值
print(v.value)
l.release() #释放
def multicore():
v = mp.Value('i',0) #定义共享变量
l = mp.Lock()
#一个迭代值是1,一个迭代值是3
p1 = mp.Process(target=job,args=(v,1,l))
p2 = mp.Process(target=job,args=(v,3,l))
#不加进程锁,看如何抢资源
p1.start()
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
multicore()
结果:显然,进程锁保证了进程p1
的完整运行,然后才进行了进程p2
的运行
1
2
3
4
5
8
11
14
17
20
[Finished in 1.3s]