python 并发编程 进程池与线程池
一 进程池与线程池
1.为什么需要进程池和线程池
基于多进程或多线程实现并发的套接字通信,然而这种实现方式的致命缺陷是:
服务端的程序运行在一台机器身上,一台机器性能是有极限的,不能无限开线程
服务的开启的进程数或线程数都会随着并发的客户端数目地增多而增多,这会对服务端主机带来巨大的压力,甚至于不堪重负而瘫痪,于是我们必须对服务端开启的进程数或线程数加以控制,让机器在一个自己可以承受的范围内运行
2.线程池和进程池作用
这就是进程池或线程池的用途,例如进程池,就是用来存放进程的池子,本质还是基于多进程,只不过是对开启进程的数目加上了限制
进程池和线程池作用 ,限制开启的线程数据,进程数,保证机器在一个可承受范围,保持主机健康状态
3.什么时候开启线程和进程
计算机密集型程序用多进程,io密集型用多线程
介绍
官网:https://docs.python.org/dev/library/concurrent.futures.html
concurrent.futures模块提供了高度封装的异步调用接口
ThreadPoolExecutor:线程池,提供异步调用
ProcessPoolExecutor: 进程池,提供异步调用
Both implement the same interface, which is defined by the abstract Executor class.
基本方法
基本方法
1、submit(fn, *args, **kwargs)
异步提交任务
2、map(func, *iterables, timeout=None, chunksize=1) func是函数,
取代for循环submit的操作
3、shutdown(wait=True)
相当于进程池的pool.close()+pool.join()操作
wait=True,等待池内所有任务执行完毕回收完资源后才继续 默认不指定就是wait=True
wait=False,立即返回,并不会等待池内的任务执行完毕
但不管wait参数为何值,整个程序都会等到所有任务执行完毕
submit和map必须在shutdown之前
4、result(timeout=None)
取得结果
5、add_done_callback(fn)
回调函数
shutdown源码
def shutdown(self, wait=True):
with self._shutdown_lock:
self._shutdown_thread = True
submit() 异步提交任务
什么是异步提交任务?
异步方式指的是 提交完任务后,不用再等着任务,执行拿到结果。提交完任务后就不管这个任务有没有起来,也不用等结果,
只负责提交任务
把任务扔到池子,池子帮忙开启线程或者进程
例如现在:池子收到10个任务,但池子限制4个进程或者线程,同一时间只能开4个进程 或者4个线程 只能执行4个任务,其他任务
只能等(阻塞),一个任务执行完,再给下一个等待任务执行,走一个进一个,池子就开启4个线程 ,4个线程把10个任务完成
二 进程池
用法
from concurrent.futures import ProcessPoolExecutor import time import os import random def task(name): print("name:%s pid:%s run" %(name, os.getpid())) time.sleep(random.randint(1,3)) if __name__ == "__main__": # 把进程池造出来,指定池的大小 # 指定4个进程 pool = ProcessPoolExecutor(4) for i in range(1,11): pool.submit(task,"子进程%s" %i) print("主") ''' 主 name:子进程1 pid:8012 run name:子进程2 pid:14540 run name:子进程3 pid:16832 run name:子进程4 pid:5984 run name:子进程5 pid:14540 run name:子进程6 pid:5984 run name:子进程7 pid:8012 run name:子进程8 pid:14540 run name:子进程9 pid:16832 run name:子进程10 pid:8012 run '''
先打印主,提交任务是异步提交方式,提交完任务以后,什么都不要管,不管进程起来没有,也不打印结果,提交完就走,这就是异步提交
并且看到pid 开启4个pid,一直都是使用开启的4个pid,没有启动新的进程,一直都是刚开始的4个进程
二
主进程想等到 进程池所有任务都执行完后,主进程再执行, 相当于join方法
使用shutdown()
把往池里提交任务的入口关闭掉,不能提交任务到池子里,等到池子所有任务执行完,才能执行主进程,
三.为什么join要把池子入口关闭掉?
shutdown函数在维护一个计数器,把池子关闭,不能往池子提交任务,因为要明确统计现在池子拿到有几个任务,例如10个任务,
走一个任务,计数器就减1,直到计数器为0,池子入口就开放
from concurrent.futures import ProcessPoolExecutor import time import os import random def task(name): print("name:%s pid:%s run" %(name, os.getpid())) time.sleep(random.randint(1,3)) if __name__ == "__main__": # 把进程池造出来,指定池的大小 # 指定4个进程 pool = ProcessPoolExecutor(4) for i in range(1,11): pool.submit(task,"子进程%s" %i) pool.shutdown() print("主") ''' name:子进程1 pid:17744 run name:子进程2 pid:18208 run name:子进程3 pid:12440 run name:子进程4 pid:19396 run name:子进程5 pid:18208 run name:子进程6 pid:12440 run name:子进程7 pid:18208 run name:子进程8 pid:17744 run name:子进程9 pid:19396 run name:子进程10 pid:12440 run 主 '''
三 线程池
用法
把ProcessPoolExecutor换成ThreadPoolExecutor,其余用法全部相同
from concurrent.futures import ThreadPoolExecutor import time import os import random def task(name): print("name:%s pid:%s run" %(name, os.getpid())) time.sleep(random.randint(1,3)) if __name__ == "__main__": # 把进程池造出来,指定池的大小 # 指定4个进程 pool = ThreadPoolExecutor(4) for i in range(1,11): pool.submit(task,"子线程%s" %i) pool.shutdown() print("主") ''' name:子线程1 pid:13736 run name:子线程2 pid:13736 run name:子线程3 pid:13736 run name:子线程4 pid:13736 run name:子线程5 pid:13736 run name:子线程6 pid:13736 run name:子线程7 pid:13736 run name:子线程8 pid:13736 run name:子线程9 pid:13736 run name:子线程10 pid:13736 run 主 '''
map方法
executor.map(task,range(1,12)) #map取代了for+submit