前言
进程和线程,有很多地方非常类似,包括使用的方法也很多相同的,
所以我决定放到一起对比学习,
这一篇,专门对比:
进程池
线程池
进程池
为什么会有进程池?
- 1,因为每次开启一个进程,都需要创建一个内存空间,这是耗时的
- 2,进程过多,操作调度也会耗时,
- 所以会有非常大的性能问题,
- 所以我们不会让进程太大,我们会设计一个进程池,
进程池的使用
- 1,Python中先创建一个进程的池子,
- 2,这个进程池能存放多少个进程,比如有5个进程,
- 3,先把这些进程创建好,
- 4,比如有50个任务他们到进程池里面去找进程,找到的就执行,找不到的就等待,
- 5,进程执行结束之后,不会结束,而是返回进程池,等待下一个任务,
- 所以进程池,可以节省进程创建的时间,节省了操作系统的调度,而且进程不会过多的创建,
进程池的使用:进程池的同步调用:p.apply
import os,time
from multiprocessing import Pool
def work(n):
print('%s run' %os.getpid())
time.sleep(1)
return n**2
if __name__ == '__main__':
p=Pool(3) #进程池中从无到有创建三个进程,以后一直是这三个进程在执行任务
res_l=[]
for i in range(10):
res=p.apply(work,args=(i,)) # 同步调用,直到本次任务执行完毕拿到res,等待任务work执行的过程中可能有阻塞也可能没有阻塞
# 但不管该任务是否存在阻塞,同步调用都会在原地等着
res_l.append(res)
print(res_l) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
同步调用,就是串行,所以得到最终的结果需要10秒
进程池的使用:进程池的异步调用:p.apply_async
import os
import time
import random
from multiprocessing import Pool
def work(n):
print('%s run' %os.getpid())
time.sleep(random.random())
return n**2
if __name__ == '__main__':
p=Pool(3) #进程池中从无到有创建三个进程,以后一直是这三个进程在执行任务
res_l=[]
for i in range(10):
res=p.apply_async(work,args=(i,)) # 异步运行,根据进程池中有的进程数,每次最多3个子进程在异步执行
# 返回结果之后,将结果放入列表,归还进程,之后再执行新的任务
# 需要注意的是,进程池中的三个进程不会同时开启或者同时结束
# 而是执行完一个就释放一个进程,这个进程就去接收新的任务。
res_l.append(res)
# 异步apply_async用法:如果使用异步提交的任务,主进程需要使用jion,等待进程池内任务都处理完,然后可以用get收集结果
# 否则,主进程结束,进程池可能还没来得及执行,也就跟着一起结束了
p.close()
p.join()
for res in res_l:
print(res.get()) #使用get来获取apply_aync的结果,如果是apply,则没有get方法,因为apply是同步执行,立刻获取结果,也根本无需get
进程池的返回值
from multiprocessing import Pool, Process
def func(i):
return i
# if __name__ == '__main__':
# pool = Pool(5)
# res_list = []
# for i in range(10):
# # res = pool.apply(func,args=(i,)) # 所以这个结果接收,就是返回值,
# res = pool.apply_async(func,args=(i,)) # 所以这个结果接收,就是返回值,
# res_list.append(res)
# for res in res_list:
# print(res.get()) # get会阻塞等待结果
# 上面讲了apply和apply_async 的返回值的问题,
# 下面讲讲map的返回值的问题,比较简单
if __name__ == '__main__':
pool = Pool(5)
ret = pool.map(func,range(10))
print(ret) # 这是返回了一个列表,
# 使用的时候想用map,map搞不定就使用,apply_async
进程池的回调函数
from multiprocessing import Pool
def func1(n):
print(111)
return n
def func2(n):
print(222)
print(n*2)
if __name__ == '__main__':
p = Pool(5)
p.apply_async(func1,args=(10,),callback=func2)
p.close()
p.join()
# 回调函数都是在主进程中执行的,
总结
- 更高级的进程池是比较智能的,
- 比如现在进程池有5个进程,就可以处理过来了,就不需要增加
- 但是如果处理等待的任务太多了,急需要往进程池里面加进程,一直到设置的进程池上限
- 如果任务减少了,就进程池里面减少,
- 这是比较智能的,
- Python中没有高级的进程池,只有一个固定的进程数的进程池,没有弹性的那种,
线程池
1,可以使用第三方的模块threadpool,这个需要安装,pip install threadpool
2,可以使用内置模块的线程池,from concurrent.futures import ThreadPoolExecutor
池 —— concurrent.futures
Python标准模块--concurrent.futures
concurrent.futures模块提供了高度封装的异步调用接口,其中:
ThreadPoolExecutor:线程池
ProcessPoolExecutor: 进程池
借助上面两个类,我们可以很方便地创建进程池对象和线程池对象。
p_pool = ProcessPoolExecutor(max_workers=5) # 创建一个最多5个woker的进程池
t_pool = ThreadPoolExecutor(max_workers=5) # 创建一个最多5个woker的线程池
线程池的使用:
from concurrent.futures import ThreadPoolExecutor
import time
def func(n):
time.sleep(2)
print(n)
return n*n
tpool = ThreadPoolExecutor(max_workers=5)
# 进程池,启动cpu核数+1.
# 而线程池的启动是cpu核数 * 5 不要超过这个,
t_list = []
for i in range(20):
t = tpool.submit(func,i) # 提交一个任务,传递一个参数,
t_list.append(t)
tpool.shutdown()
print("主进程")
for t in t_list:
print(t.result())
可用方法介绍:
基本方法
submit(fn, *args, **kwargs) 提交任务
map(func, *iterables, timeout=None, chunksize=1) 取代for循环submit的操作
shutdown(wait=True) 相当于进程池的pool.close()+pool.join()操作
shutdown做了两个事情:
1,colse 关闭这个池子,不让有任务进来,
2,join是阻塞,直到这个池子的任务执行完,
所以是一个shutdown做了两个事情,
wait=True,等待池内所有任务执行完毕回收完资源后才继续
wait=False,立即返回,并不会等待池内的任务执行完毕
但不管wait参数为何值,整个程序都会等到所有任务执行完毕
submit和map必须在shutdown之前
result(timeout=None) 取得结果
add_done_callback(fn) 回调函数
技术改变命运