Python通过future处理并发问题


Posted in Python onOctober 17, 2017

future初识

通过下面脚本来对future进行一个初步了解:

例子1:普通通过循环的方式

import os
import time
import sys
import requests
POP20_CC = (
 "CN IN US ID BR PK NG BD RU JP MX PH VN ET EG DE IR TR CD FR"
).split()
BASE_URL = 'http://flupy.org/data/flags'
DEST_DIR = 'downloads/'
def save_flag(img,filename):
 path = os.path.join(DEST_DIR,filename)
 with open(path,'wb') as fp:
 fp.write(img)
def get_flag(cc):
 url = "{}/{cc}/{cc}.gif".format(BASE_URL,cc=cc.lower())
 resp = requests.get(url)
 return resp.content
def show(text):
 print(text,end=" ")
 sys.stdout.flush()
def download_many(cc_list):
 for cc in sorted(cc_list):
 image = get_flag(cc)
 show(cc)
 save_flag(image,cc.lower()+".gif")
 return len(cc_list)
def main(download_many):
 t0 = time.time()
 count = download_many(POP20_CC)
 elapsed = time.time()-t0
 msg = "\n{} flags downloaded in {:.2f}s"
 print(msg.format(count,elapsed))
if __name__ == '__main__':
 main(download_many)

    例子2:通过future方式实现,这里对上面的部分代码进行了复用

from concurrent import futures
from flags import save_flag, get_flag, show, main
MAX_WORKERS = 20
def download_one(cc):
 image = get_flag(cc)
 show(cc)
 save_flag(image, cc.lower()+".gif")
 return cc
def download_many(cc_list):
 workers = min(MAX_WORKERS,len(cc_list))
 with futures.ThreadPoolExecutor(workers) as executor:
 res = executor.map(download_one, sorted(cc_list))
 return len(list(res))
if __name__ == '__main__':
 main(download_many)

    分别运行三次,两者的平均速度:13.67和1.59s,可以看到差别还是非常大的。

future

future是concurrent.futures模块和asyncio模块的重要组件

从python3.4开始标准库中有两个名为Future的类:concurrent.futures.Future和asyncio.Future
这两个类的作用相同:两个Future类的实例都表示可能完成或者尚未完成的延迟计算。与Twisted中的Deferred类、Tornado框架中的Future类的功能类似

注意:通常情况下自己不应该创建future,而是由并发框架(concurrent.futures或asyncio)实例化

原因:future表示终将发生的事情,而确定某件事情会发生的唯一方式是执行的时间已经安排好,因此只有把某件事情交给concurrent.futures.Executor子类处理时,才会创建concurrent.futures.Future实例。
如:Executor.submit()方法的参数是一个可调用的对象,调用这个方法后会为传入的可调用对象排定时间,并返回一个

future

客户端代码不能应该改变future的状态,并发框架在future表示的延迟计算结束后会改变期物的状态,我们无法控制计算何时结束。

这两种future都有.done()方法,这个方法不阻塞,返回值是布尔值,指明future链接的可调用对象是否已经执行。客户端代码通常不会询问future是否运行结束,而是会等待通知。因此两个Future类都有.add_done_callback()方法,这个方法只有一个参数,类型是可调用的对象,future运行结束后会调用指定的可调用对象。

.result()方法是在两个Future类中的作用相同:返回可调用对象的结果,或者重新抛出执行可调用的对象时抛出的异常。但是如果future没有运行结束,result方法在两个Futrue类中的行为差别非常大。

对concurrent.futures.Future实例来说,调用.result()方法会阻塞调用方所在的线程,直到有结果可返回,此时,result方法可以接收可选的timeout参数,如果在指定的时间内future没有运行完毕,会抛出TimeoutError异常。

而asyncio.Future.result方法不支持设定超时时间,在获取future结果最好使用yield from结构,但是concurrent.futures.Future不能这样做

不管是asyncio还是concurrent.futures.Future都会有几个函数是返回future,其他函数则是使用future,在最开始的例子中我们使用的Executor.map就是在使用future,返回值是一个迭代器,迭代器的__next__方法调用各个future的result方法,因此我们得到的是各个futrue的结果,而不是future本身

关于future.as_completed函数的使用,这里我们用了两个循环,一个用于创建并排定future,另外一个用于获取future的结果

from concurrent import futures
from flags import save_flag, get_flag, show, main
MAX_WORKERS = 20
def download_one(cc):
 image = get_flag(cc)
 show(cc)
 save_flag(image, cc.lower()+".gif")
 return cc
def download_many(cc_list):
 cc_list = cc_list[:5]
 with futures.ThreadPoolExecutor(max_workers=3) as executor:
 to_do = []
 for cc in sorted(cc_list):
  future = executor.submit(download_one,cc)
  to_do.append(future)
  msg = "Secheduled for {}:{}"
  print(msg.format(cc,future))
 results = []
 for future in futures.as_completed(to_do):
  res = future.result()
  msg = "{}result:{!r}"
  print(msg.format(future,res))
  results.append(res)
 return len(results)
if __name__ == '__main__':
 main(download_many)

    结果如下:

Python通过future处理并发问题

注意:Python代码是无法控制GIL,标准库中所有执行阻塞型IO操作的函数,在等待操作系统返回结果时都会释放GIL.运行其他线程执行,也正是因为这样,Python线程可以在IO密集型应用中发挥作用

以上都是concurrent.futures启动线程,下面通过它启动进程

concurrent.futures启动进程

concurrent.futures中的ProcessPoolExecutor类把工作分配给多个Python进程处理,因此,如果需要做CPU密集型处理,使用这个模块能绕开GIL,利用所有的CPU核心。

其原理是一个ProcessPoolExecutor创建了N个独立的Python解释器,N是系统上面可用的CPU核数。

使用方法和ThreadPoolExecutor方法一样

总结

Python 相关文章推荐
Python ljust rjust center输出
Sep 06 Python
python的迭代器与生成器实例详解
Jul 16 Python
简介Django框架中可使用的各类缓存
Jul 23 Python
详解Python之数据序列化(json、pickle、shelve)
Mar 30 Python
详解python多线程、锁、event事件机制的简单使用
Apr 27 Python
python+selenium实现简历自动刷新的示例代码
May 20 Python
python 求一个列表中所有元素的乘积实例
Jun 11 Python
详解python实现交叉验证法与留出法
Jul 11 Python
Python模块future用法原理详解
Jan 20 Python
在PyCharm中实现添加快捷模块
Feb 12 Python
python+openCV对视频进行截取的实现
Nov 27 Python
python实现简单聊天功能
Jul 07 Python
python3设计模式之简单工厂模式
Oct 17 #Python
基于Python和Scikit-Learn的机器学习探索
Oct 16 #Python
python版简单工厂模式
Oct 16 #Python
Python实现扩展内置类型的方法分析
Oct 16 #Python
Python使用文件锁实现进程间同步功能【基于fcntl模块】
Oct 16 #Python
python利用paramiko连接远程服务器执行命令的方法
Oct 16 #Python
基于使用paramiko执行远程linux主机命令(详解)
Oct 16 #Python
You might like
分享一个Laravel好用的Cache宏
2015/03/02 PHP
PHP JSON格式的中文显示问题解决方法
2015/04/09 PHP
ThinkPHP中调用PHPExcel的实现代码
2017/04/08 PHP
jQuery示例收集
2010/11/05 Javascript
javascript模拟地球旋转效果代码实例
2013/12/02 Javascript
jquery操作 iframe的方法
2014/12/03 Javascript
jQuery实现监控页面所有ajax请求的方法
2015/12/10 Javascript
基于JavaScript实现div层跟随滚动条滑动
2016/01/12 Javascript
老生常谈遮罩层 滚动条的问题
2016/04/29 Javascript
js实现密码强度检验
2017/01/15 Javascript
JS打开摄像头并截图上传示例
2017/02/18 Javascript
bootstrap 设置checkbox部分选中效果
2017/04/20 Javascript
jQuery zTree树插件动态加载实例代码
2017/05/11 jQuery
Angular实现下载安装包的功能代码分享
2017/09/05 Javascript
vue.js实例对象+组件树的详细介绍
2017/10/20 Javascript
Node.js笔记之process模块解读
2018/05/31 Javascript
vue-cli3 karma单元测试的实现
2019/01/18 Javascript
pm2发布node配置文件ecosystem.json详解
2019/05/15 Javascript
[00:32]2016完美“圣”典风云人物:Maybe宣传片
2016/12/05 DOTA
python同时给两个收件人发送邮件的方法
2015/04/30 Python
python实现中文转换url编码的方法
2016/06/14 Python
python模拟登录并且保持cookie的方法详解
2017/04/04 Python
python逆序打印各位数字的方法
2018/06/25 Python
python 利用pandas将arff文件转csv文件的方法
2019/02/12 Python
pycharm配置QtDesigner的超详细方法
2021/01/25 Python
物流管理毕业生自荐信
2013/10/24 职场文书
网页美工求职信
2014/02/15 职场文书
党员干部2014全国两会学习心得体会
2014/03/10 职场文书
实习会计求职自荐信范文
2014/03/10 职场文书
本科毕业生求职信
2014/06/15 职场文书
单位接收函格式
2015/01/30 职场文书
2015年“公民道德宣传日”活动方案
2015/05/06 职场文书
2016年暑期教师培训心得体会
2016/01/09 职场文书
初二数学教学反思
2016/02/17 职场文书
导游词之藏龙百瀑景区
2019/12/30 职场文书
MySQL远程无法连接的一些常见原因总结
2022/09/23 MySQL