上一篇博客里对多进程进行介绍,多进程 Multiprocessing 和多线程 threading 类似, 他们都是在 python 中用来并行运算的. 不过既然有了 threading, 为什么 Python 还要出一个 multiprocessing 呢? 原因很简单, 就是用来弥补 threading 的一些劣势, 比如在 threading 教程中提到的GIL. 多进程编程利用了计算机多核CPU的优势,实现真正的并行计算。
0. 多线程threading 与多进程Multiprocessing 类比
import multiprocessing as mp
import threading as td
定义一个被线程和进程调用的函数
def job(a,d):
print('aaaaa')
创建线程和进程
t1 = td.Thread(target=job,args=(1,2))
p1 = mp.Process(target=job,args=(1,2))
注意:Thread和Process的首字母都要大写,被调用的函数没有括号,被调用的函数的参数放在args(…)中
分别启动线程和进程
t1.start()
p1.start()
分别连接线程和进程
t1.join()
p1.join()
运用
在运用时需要添加上一个定义main函数的语句
if __name__=='__main__':
完整的应用代码:
import multiprocessing as mp
def job(a,d):
print('aaaaa')
if __name__=='__main__':
p1 = mp.Process(target=job,args=(1,2))
p1.start()
p1.join()
1. 存储进程输出 Queue
Queue的功能是将每个核或线程的运算结果放在队里中, 等到每个线程或核运行完毕后再从队列中取出结果, 继续加载运算。原因很简单, 多线程调用的函数不能有返回值, 所以使用Queue存储多个线程运算的结果。
把结果放在 Queue 里
定义一个被多线程调用的函数,q
就像一个队列,用来保存每次函数运行的结果
#该函数没有返回值!!!
def job(q):
res=0
for i in range(1000):
res+=i+i**2+i**3
q.put(res) #queue
完整的代码
import multiprocessing as mp
def job(q):
res=0
for i in range(1000):
res+=i+i**2+i**3
q.put(res) #queue
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+res2)
2. 性能对比
创建多进程 multiprocessing
和上节一样,首先import multiprocessing
并定义要实现的job()
,同时为了容易比较,我们将计算的次数增加到1000000
import multiprocessing as mp
def job(q):
res = 0
for i in range(1000000):
res += i + i**2 + i**3
q.put(res) # queue
因为多进程是多核运算,所以我们将上节的多进程代码命名为multicore()
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)
创建多线程 multithread
接下来创建多线程程序,创建多线程和多进程有很多相似的地方。首先import threading
然后定义multithread()
完成同样的任务
import threading as td
def multithread():
q = mp.Queue() # thread可放入process同样的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('multithread:', res1 + res2)
创建普通函数
最后我们定义最普通的函数。注意,在上面例子中我们建立了两个进程或线程,均对job()
进行了两次运算,所以在normal()
中我们也让它循环两次
def normal():
res = 0
for _ in range(2):
for i in range(1000000):
res += i + i**2 + i**3
print('normal:', res)
运行时间
最后,为了对比各函数运行时间,我们需要import time
, 然后依次运行定义好函数:
import time
if __name__ == '__main__':
st = time.time()
normal()
st1 = time.time()
print('normal time:', st1 - st)
multithread()
st2 = time.time()
print('multithread time:', st2 - st1)
multicore()
print('multicore time:', time.time() - st2)
下面我们来看下实际运行对比。
普通/多线程/多进程的运行时间分别是1.13
,1.3
和0.64
秒。 我们发现多核/多进程最快,说明在同时间运行了多个任务。 而多线程的运行时间居然比什么都不做的程序还要慢一点。