为什么需要线程池呢?
代码语言:javascript复制对于io密集型,提高执行的效率。
线程的创建是需要消耗系统资源的。
每个线程各自分配一个任务,剩下的任务排队等待,
当某个线程完成了任务的时候,排队任务就可以安排给这个线程继续执行。
如何来实现线程池呢?
代码语言:javascript复制标准库concurrent.futures模块
它提供了ThreadPoolExecutor和ProcessPoolExecutor两个类,
分别实现了对threading模块和multiprocessing模块的进一步抽象。
不仅可以帮我们自动调度线程,还可以做到:
- 主线程可以获取某一个线程(或者任务)的状态,以及返回值
- 当一个线程完成的时候,主线程能够立即知道
- 让多线程和多进程的编码接口一致
如何用呢?简单代码演示
代码语言:javascript复制from concurrent.futures import ThreadPoolExecutor
def get(run):
print(" {}finished".format(run))
# 创建线程池
# 设置线程池中最多能同时运行的线程数目,其他等待
executor = ThreadPoolExecutor(max_workers=2)
# 通过submit函数提交执行的函数到线程池中,submit函数立即返回,不阻塞
# task1和task2是任务句柄
task1 = executor.submit( get, (2) )
task2 = executor.submit( get, (3) )
结果展示
代码语言:javascript复制 2finished
3finished
代码语言:javascript复制# done()方法用于判断某个任务是否完成,bool型,完成返回True,没有完成返回False
print( task1.done() )
# cancel()方法用于取消某个任务,该任务没有放到线程池中才能被取消,如果已经放进线程池子中,则不能被取消
# bool型,成功取消了返回True,没有取消返回False
print( task2.cancel() )
# result()方法可以获取task的执行结果,get()函数有返回值
print( task1.result() )
print( task2.result() )
看下最后的展示
代码语言:javascript复制True
False
None
None
因为没有返回结果,所以打印的是None
代码语言:javascript复制ThreadPoolExecutor类在构造实例的时候,传入max_workers参数来设置线程池中最多能同时运行的线程数目
使用submit()函数来提交线程需要执行任务(函数名和参数)到线程池中,并返回该任务的句柄,
注意submit()不是阻塞的,而是立即返回。
通过submit()函数返回的任务句柄,能够使用done()方法判断该任务是否结束,使用cancel()方法来取消,
使用result()方法可以获取任务的返回值,查看内部代码,发现该方法是阻塞的
ProcessPoolExecutor多进程
代码语言:javascript复制
from concurrent.futures import ProcessPoolExecutor
def get(run):
print(" {}finished".format(run))
if __name__ == '__main__':
p = ProcessPoolExecutor(4) # 设置
for i in range(10):
# 同步调用方式,不仅要调用,还要等返回值
obj = p.submit(get, "参数") # 传参方式(任务名,参数),参数使用位置或者关键字参数
res = obj.result()
p.shutdown(wait=True) # 关闭进程池的入口,等待池内任务运行结束
print("主")
结果打印;
代码语言:javascript复制 参数finished
参数finished
参数finished
参数finished
参数finished
参数finished
参数finished
参数finished
参数finished
参数finished
主
线程池和进程池如何选择呢?
进程池:异步 回调函数,cpu密集型,同时执行,每个进程有不同的解释器和内存空间,互不干扰
代码语言:javascript复制
from concurrent.futures import ProcessPoolExecutor
def get(run):
print(" {}finished".format(run))
def future(future):
print(future.result())
if __name__ == '__main__':
p = ProcessPoolExecutor(4) # 设置
for i in range(10):
# 同步调用方式,不仅要调用,还要等返回值
obj = p.submit(get, "参数") # 传参方式(任务名,参数),参数使用位置或者关键字参数
obj.add_done_callback(future)
p.shutdown(wait=True) # 关闭进程池的入口,等待池内任务运行结束
print("主")
常用的就是我们的爬虫。爬去后解析,解析后存储,会大量使用的cpu。
(2)线程池:异步 回调函数,IO密集型主要使用方式,线程池:执行操作为谁有空谁执行
代码语言:javascript复制from concurrent.futures import ThreadPoolExecutor
def get(run):
print(" {}finished".format(run))
def future(future):
print(future.result())
if __name__ == '__main__':
p = ThreadPoolExecutor(4)
start = time.time()
testcase=[2,3,4,5]
for case in testcase:
futu = p.submit(get, case)
futu.add_done_callback(future)
p.shutdown(wait=True)
结果展示:
代码语言:javascript复制 2finished
None
3finished
4finished
None
None
5finished
None
使用:移动端多用例并行执行的时候,需要设备空闲才执行,我们可以用线程来管理一个设备,设备执行完毕 就下发一个任务。这个最好的方式 可以和队列放在一起去执行。
总结
代码语言:javascript复制线程不是越多越好,会涉及cpu上下文的切换(会把上一次的记录保存)。
进程比线程消耗资源,进程相当于一个工厂,工厂里有很多人,里面的人共同享受着福利资源,一个进程里默认只有一个主线程,
计算密度型适用于多进程
线程:线程是计算机中工作的最小单元
进程:默认有主线程 (帮工作)可以多线程共存