【问题描述】
在上一个活动中,对50个公司的商誉信息按固定的先后顺序依次轮流爬取。所有任务串行执行,是单线程方式,其耗用的时间就是每一个任务的耗费时间之和。因此爬取速度较慢,耗费时间较长。为了提高程序执行效率,采取多线程并发方式执行程序,使多个爬取任务并发执行,节省程序执行时间,提高程序执行效率。
输出结果:
运行1次get_all_mt函数花费时间:0.43199610710144043秒
【题前思考】
根据问题描述,填写表8-1-4。
表8-1-4 问题分析
【解题思路】
直接调用上一活动中定义的get_proportion_of_goodwill( )函数,负责获取某个公司的商誉信息。使用concurrent.futures模块中的ThreadPoolExecutor类来并发执行函数。首先创建ThreadPoolExecutor类对象,然后调用该对象的map( )方法并发执行指定的函数。map( )方法主要使用两个参数,一是要并发执行的函数,二是每次调用函数要用到的参数构成的可迭代对象(英文名称iterable,是指可以用for...in方式访问其中每个元素的对象)。多个函数并发执行的过程中会从可迭代对象中取出项作为自己的参数调用函数,并将每次调用的返回值组成一个列表返回。
【参考代码】
【代码分析】
①:从concurrent.futures模块中导入类ThreadPoolExecutor用于多线程执行函数。
②:定义函数get_all_mt( )以多线程方式爬取指定内容,参数stocklist是股票代码构成的列表。
③:创建一个名为executor的ThreadPoolExecutor类对象,参数max_workers=10表示最多同时执行10个线程。with语句表示创建并初始化其后的对象,然后执行with下面的语句块,最后无论是否发生异常都会完成对象的清理。
④:调用executor对象的map( )方法,方法的第一个参数是被调用函数,第二个参数是被调用函数的参数构成的列表。map( )方法每次执行被调用函数时都会从列表中取1项作为被调用函数的参数。因为get_proportion_of_goodwill( )函数的参数就是一个字符串表示的股票代码,所以stocklist就是股票代码列表。map( )方法将每次执行被调用函数的返回值保存到列表,并在运行结束之后将列表返回。本例将各公司的商誉占比列表返回并保存到变量res。
【技术全貌】
concurrent.futures模块提供了非常方便的多线程和多进程操作方法,其中的ThreadPool Executor类不仅有map( )方法,还有其他方法可以用于并发执行多个线程。ThreadPoolExecutor类中的常用方法见表8-1-5。
表8-1-5 ThreadPoolExecutor类的用法
concurrent.futures.ThreadPoolExecutor类只能执行多个不相关的线程,如果线程之间需要交互就要使用threading模块中的线程类Thread。Thread类用来创建和管理线程实例,线程的创建和管理方法见表8-1-6。
表8-1-6 线程Thread类的创建和管理方法