concurrent.futures模块提供了高度封装的异步调用接口
ThreadPoolExecutor:线程池,提供异步调用
ProcessPoolExecutor: 进程池,提供异步调用
下面的例子,使用的sbumit方法调用。使用 add_done_callback 添加回调函数。
如果不使用回调函数,可以用 future.result() 获取返回的结果。一般2种方法用一种就可以了。
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import ProcessPoolExecutor
## 定义回调函数
def callback (res):
res = res.result() ## 这里获取到的函数的返回值
url = res['url']
...
## 定义处理函数
def parse_data(data):
...
return {"":"","":""}
executor = ProcessPoolExecutor(max_workers=3) ## 定义了最大并发量是 3 使用进程池
executor = ThreadPoolExecutor(max_workers=3) ## 定义了最大并发量是 3 使用线程池
futures = []
for url in urls:
future = executor.submit(parse_data, url).add_done_callback(callback)
futures.append(future) ## futures 用于存放后面的执行结果,便于打印,如果不需要的话可以不写
for future in concurrent.futures.as_completed(futures):
print(future.result())
if future.exception():
print(future.exception())
executor.shutdown(True)
## 也可以使用map的方法来做
futures = executor.map(parse_data, urls)
for future in futures:
logging.info(future)
future.result() 是函数返回的返回值
如果是 IO 密集多尽量用多线程, CPU 密集尽量用多进程.
map 函数会比 submit 更简洁, 但是没法对返回一个一个处理, 而 submit 的好处是可以对每个返回在执行完成的瞬间处理, 不用等到每个线程/进程都执行完毕.
运用多进程/多线程的时候函数报错并不一定会直接结束程序, 而有可能会什么都不发生, 这样需要在 as_completed 之后捕捉异常, 用上面代码所示语句就可以.
网上找来的一段例子
def get_data(self, workers=5):
self.cookie = self.get_cookie(self)
total_data = {
'data' : [],
'status_code': 0
}
with concurrent.futures.ProcessPoolExecutor(max_workers=workers) as executor:
futures = [executor.submit(self.analyze, patent_number) for patent_number in self.patent_number_list]
for future in concurrent.futures.as_completed(futures):
# add result to total data
total_data['data'].append(future.result())
return total_data