一、线程池与进程池的区别
两个关键词:线程,进程/池
池:可以将其理解为一种容器,有其固定的大小
什么时候用线程池/进程池,分两个问题讨论
1.什么时候用池:当程序中的任务并发数远远大于计算机的承受能力时,就应该用池的概念将开启的进程数或者线程数限制在计算机的承受范围之内
2.用什么样的池:用进程池还是线程池取决于程序的类型,对于IO密集型—>线程,对于计算密集型—>进程
这就涉及到并发和并行概念的区别。并发,是指一个处理器(进程)能同时处理多个任务;并行,指的是多个处理器(进程)同时处理多个不同的任务。一般可以认为多线程用于并发任务处理,而多进程用于并行任务处理。因此,当任务是IO型的,适合用线程池的方式,因为一个线程被IO挂起,其他的线程可以继续执行;而如果任务是CPU型的,适合用进程池的方式,此时并未挂起,需要同时执行任务。
二、实用案例
下载网页的代码为例,由于是IO密集型的,可以使用线程池
代码:
# 参数times用来模拟网络请求的时间
def get_html(i, times=5):
print("get page {} start".format(i))
if i == 2:
times *= 10
time.sleep(times) # 模拟IO行为
print("get page {} finished".format(i))
return i, times
t0 = time.time()
for i in range(5):
get_html(i)
t00 = time.time()
print(f'normal exe time:{t00 - t0}')
executor = ThreadPoolExecutor(max_workers=3)
# 通过submit函数提交执行的函数到线程池中,submit函数立即返回,不阻塞
tasks = [executor.submit(get_html, i) for i in range(5)]
# result方法可以获取task的执行结果
t1 = time.time()
for task in tasks:
print(task.result())
t2 = time.time()
print(f'thread pool exe time:{t2 - t1}')
结果如下:
get page 0 start
get page 1 start
get page 2 start
get page 0 finished
get page 3 start
(0, 5)
get page 1 finished
get page 4 start
(1, 5)
get page 3 finished
get page 4 finished
get page 2 finished
(2, 50)
(3, 5)
(4, 5)
thread pool exe time:50.0
结论:
1、有加速的作用
存在的问题:
task.result一定要等待前面任务的结果返回,才能获取后面任务的结果,这里可以用as_complete优化
加入as_complete如下:
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
# 参数times用来模拟网络请求的时间
def get_html(i, times=5):
print("get page {} start".format(i))
if i == 2:
times *= 10
time.sleep(times) # 模拟IO行为
print("get page {} finished".format(i))
return i, times
t0 = time.time()
for i in range(5):
get_html(i)
t00 = time.time()
print(f'normal exe time:{t00 - t0}')
executor = ThreadPoolExecutor(max_workers=3)
# 通过submit函数提交执行的函数到线程池中,submit函数立即返回,不阻塞
tasks = [executor.submit(get_html, i) for i in range(5)]
# result方法可以获取task的执行结果
t1 = time.time()
for task in as_completed(tasks):
print(task.result())
t2 = time.time()
print(f'thread pool exe time:{t2 - t1}')
get page 0 start
get page 0 finished
get page 1 start
get page 1 finished
get page 2 start
get page 2 finished
get page 3 start
get page 3 finished
get page 4 start
get page 4 finished
normal exe time:70.0
get page 0 start
get page 1 start
get page 2 start
get page 0 finished
get page 3 start
(0, 5)
get page 1 finished
get page 4 start
(1, 5)
get page 3 finished
(3, 5)
get page 4 finished
(4, 5)
get page 2 finished
(2, 50)
thread pool exe time:50.0
结论:as_completed()方法是一个生成器,在没有任务完成的时候,会阻塞,在有某个任务完成的时候,会yield这个任务,就能执行for循环下面的语句,然后继续阻塞住,循环到所有的任务结束。从结果也可以看出,先完成的任务会先通知主线程