两个关键词:线程,进程/池
池:可以将其理解为一种容器,有其固定的大小
什么时候用线程池/进程池,分两个问题讨论
1.什么时候用池:当程序中的任务并发数远远大于计算机的承受能力时,就应该用池的概念将开启的进程数或者线程数限制在计算机的承受范围之内
2.用什么样的池:用进程池还是线程池取决于程序的类型,对于IO密集型--->线程,对于计算密集型--->进程
两个概念
提交任务的两种方式:同步与异步
同步:提交完任务后,就在原地等待,直到该任务运行完拿到返回值后,才执行下一行代码--->导致任务的运行方式为串行
异步:提交完任务后,就立即执行下一行代码,当任务有返回值后,自动触发回调函数--->导致任务的运行方式为并发
程序的两种运行状态:阻塞与非阻塞
阻塞:IO阻塞
非阻塞:就绪与运行
注意:同步与阻塞并无关联
两者虽然给人都有等的感觉,但是同步是一种提交任务的方式,要等到任务运行完拿到返回值后才执行下一行代码,其中等待的任务可能只是纯计算型的(计算的数据量大造成等待),而阻塞是IO阻塞
如何使用线程/进程池
进程池
import os,time from concurrent.futures import ProcessPoolExecutor def task(): sum = 0 print('%s正在产生数据...'%os.getpid()) for i in range(1000000): sum +=i return sum def dispose(sum): print('%s正在处理数据...'%os.getpid()) time.sleep(1) # 模拟处理数据 if __name__ == '__main__': p = ProcessPoolExecutor(4) for i in range(10): # 有十个任务需要并发处理,但是假设计算机无法承受开启10个进程 feture=p.submit(task) # 提交任务,并得到一个feture对象 feture.add_done_callback(dispose) # 为其绑定回调函数,并将feture对象作为参数传入,会在任务执行完后自动触发 p.shutdown() # 关闭进程池的入口,并等待任务执行结束 print('主',os.getpid()) # ps: 如果feture = p.submit(task).result() 为同步提交方式
运行结果
10932正在产生数据... 3408正在产生数据... 8576正在产生数据... 10932正在产生数据... 1920正在处理数据... 3408正在产生数据... 8576正在产生数据... 10732正在产生数据... 1920正在处理数据... 10932正在产生数据... 3408正在产生数据... 8576正在产生数据... 1920正在处理数据... 1920正在处理数据... 1920正在处理数据... 1920正在处理数据... 1920正在处理数据... 1920正在处理数据... 1920正在处理数据... 1920正在处理数据... 主 1920
从运行结果来看
1.只开启了4个子进程,且执行结束后,仍然是这四个子进程干剩下的任务(谁空闲,谁就执行下一个)
2.是主进程在执行回调函数
3.这种方式相比于将dispose函数放入task而言,实现了解耦和
线程池
import time,random from concurrent.futures import ThreadPoolExecutor from threading import current_thread def task(): sum = 0 print('%s正在产生数据...' % current_thread().name) time.sleep(random.random()) # 模拟IO行为 return sum def dispose(sum): print('%s正在处理数据...' % current_thread().name) time.sleep(1) # 模拟处理数据 if __name__ == '__main__': p = ThreadPoolExecutor(4) for i in range(10): feture = p.submit(task) feture.add_done_callback(dispose) p.shutdown() print('主', current_thread().name)
ThreadPoolExecutor-0_0正在产生数据... ThreadPoolExecutor-0_1正在产生数据... ThreadPoolExecutor-0_2正在产生数据... ThreadPoolExecutor-0_3正在产生数据... ThreadPoolExecutor-0_0正在处理数据... ThreadPoolExecutor-0_1正在处理数据... ThreadPoolExecutor-0_2正在处理数据... ThreadPoolExecutor-0_3正在处理数据... ThreadPoolExecutor-0_0正在产生数据... ThreadPoolExecutor-0_1正在产生数据... ThreadPoolExecutor-0_2正在产生数据... ThreadPoolExecutor-0_3正在产生数据... ThreadPoolExecutor-0_0正在处理数据... ThreadPoolExecutor-0_3正在处理数据... ThreadPoolExecutor-0_1正在处理数据... ThreadPoolExecutor-0_2正在处理数据... ThreadPoolExecutor-0_0正在产生数据... ThreadPoolExecutor-0_0正在处理数据... ThreadPoolExecutor-0_3正在产生数据... ThreadPoolExecutor-0_3正在处理数据... 主 MainThread
从执行结果来看
1.只开启了四个线程实现并发,且剩余的任务仍然是由这四个线程完成(谁有空,谁执行)
2.与进程池不同,回调函数是由开启的子线程完成(谁有空,谁执行)