摘要:
- 进程池与线程池
- 同步调用和异步调用
- 回调函数
- 协程
一、进程池与线程池:
1、池的概念:
不管是线程还是进程,都不能无限制的开下去,总会消耗和占用资源。
也就是说,硬件的承载能力是有限度的,在保证高效率工作的同时应该还需要保证硬件的资源占用情况,所以需要给硬件设置一个上限来减轻硬件的压力,所以就有了池的概念。
2、进程池与线程池的使用方法:(进程与线程的创建基本相似,所以进程池与线程池的使用过程也基本一样)
from concurrent.futures import ProcessPoolExecutor # 导入进程池模块 from concurrent.futures import ThreadPoolExecutor # 导入线程池模块 import os import time import random # 下面以进程池为例,线程池只是使用导入模块不一样,仅此而已。 def task(name): print('name:[%s]|进程:[%s]正在运行' % (name, os.getpid())) time.sleep(random.randint(1, 3)) # 模拟进程运行耗费时间。 # 这一步的必要性:在创建进程时,会将代码以模块的方式从头到尾导入加载执行一遍 # (所以创建线程如果不写在main里面的话,这个py文件里面的所有代码都会从头到尾加载执行一遍 # 就会导致在创建进程的时候产生死循环。) if __name__ == '__main__': pool = ProcessPoolExecutor(4) # 设置线程池的大小,默认等于cpu的核心数。 for i in range(10): pool.submit(task, '进程%s' % i) # 异步提交(提交后不等待) pool.shutdown(wait=True) # 关闭进程池入口不再提交,同时等待进程池全部运行完毕。(类似join方法) print('主') # 标识一下主进程的完毕之前的语句
# 运行过程及结果: name:[进程0]|进程:[4080]正在运行 name:[进程1]|进程:[18336]正在运行 name:[进程2]|进程:[19864]正在运行 name:[进程3]|进程:[25604]正在运行 name:[进程4]|进程:[4080]正在运行 name:[进程5]|进程:[18336]正在运行 name:[进程6]|进程:[4080]正在运行 name:[进程7]|进程:[19864]正在运行 name:[进程8]|进程:[25604]正在运行 name:[进程9]|进程:[18336]正在运行 主
二、同步调用、异步调用
同步调用:提交任务,原地等待该任务执行完毕,拿到结果后再执行下一个任务,导致程序串行执行!
from concurrent.futures import ProcessPoolExecutor # 导入进程池模块 from concurrent.futures import ThreadPoolExecutor # 导入线程池模块 import os import time import random def task(name): print('name:[%s]|进程[%s]正在运行...' % (name, os.getpid())) time.sleep(random.randint(1, 3)) return '拿到[%s]|进程%s的结果...' % (name, os.getpid()) if __name__ == '__main__': pool = ProcessPoolExecutor(4) result = [] # 创建一个空列表来搜集执行结果 for i in range(10): res = pool.submit(task, '进程%s' % i).result() # 使用.result()方法得到每次的结果,同步调用 result.append(res) pool.shutdown(wait=True) for j in result: print(j) print('主进程')
# 执行结果: name:[进程0]|进程[3376]正在运行... name:[进程1]|进程[27124]正在运行... name:[进程2]|进程[10176]正在运行... name:[进程3]|进程[28636]正在运行... name:[进程4]|进程[3376]正在运行... name:[进程5]|进程[27124]正在运行... name:[进程6]|进程[10176]正在运行... name:[进程7]|进程[28636]正在运行... name:[进程8]|进程[3376]正在运行... name:[进程9]|进程[27124]正在运行... 拿到[进程0]|进程3376的结果... 拿到[进程1]|进程27124的结果... 拿到[进程2]|进程10176的结果... 拿到[进程3]|进程28636的结果... 拿到[进程4]|进程3376的结果... 拿到[进程5]|进程27124的结果... 拿到[进程6]|进程10176的结果... 拿到[进程7]|进程28636的结果... 拿到[进程8]|进程3376的结果... 拿到[进程9]|进程27124的结果... 主进程
异步调用:提交任务,不去等结果,继续执行。
from concurrent.futures import ProcessPoolExecutor import os import random import time def task(name): time.sleep(random.randint(1, 3)) print('name: %s 进程[%s]运行...' % (name, os.getpid())) if __name__ == '__main__': pool = ProcessPoolExecutor(4) for i in range(10): pool.submit(task, '进程%s' % i) # 异步调用,提交后不等待结果,继续执行代码 pool.shutdown(wait=True) print('主进程')
name: 进程3 进程[10016]运行... name: 进程0 进程[12736]运行... name: 进程1 进程[4488]运行... name: 进程2 进程[3920]运行... name: 进程5 进程[12736]运行... name: 进程6 进程[4488]运行... name: 进程4 进程[10016]运行... name: 进程9 进程[4488]运行... name: 进程8 进程[12736]运行... name: 进程7 进程[3920]运行... 主进程
三、回调函数:
上面我们在演示异步调用时候,说过提交任务不等待执行结果,继续往下执行代码,那么,执行的结果我们怎么得到呢?
可以为进程池和线程池内的每个进程或线程绑定一个函数,该函数在进程或线程的任务执行完毕后自动触发并接收任务的返回值当做参数,这个函数就是回调函数。
from concurrent.futures import ThreadPoolExecutor import time import random import requests def task(url): print('获取网站[%s]信息' % url) response = requests.get(url) # 下载页面 time.sleep(random.randint(1, 3)) return {'url': url, 'content': response.text} # 返回结果:页面地址和页面内容 futures = [] def back(res): res = res.result() # 取到提交任务的结果(回调函数固定写法) res = '网站[%s]内容长度:%s' % (res.get('url'), len(res.get('content'))) futures.append(res) return futures if __name__ == '__main__': urls = [ 'http://www.baidu.com', 'http://www.dgtle.com/', 'https://www.bilibili.com/' ] pool = ThreadPoolExecutor(4) futures = [] for i in urls: pool.submit(task, i).add_done_callback(back) # 执行完线程后,使用回调函数 pool.shutdown(wait=True) for j in futures: print(j)
获取网站[http://www.baidu.com]信息 获取网站[http://www.dgtle.com/]信息 获取网站[https://www.bilibili.com/]信息 网站[http://www.dgtle.com/]内容长度:39360 网站[https://www.bilibili.com/]内容长度:69377 网站[http://www.baidu.com]内容长度:2381