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中字典(dict)和列表(list)的排序方法实例
Jun 16 Python
Python实现HTTP协议下的文件下载方法总结
Apr 20 Python
简单讲解Python编程中namedtuple类的用法
Jun 21 Python
python实现C4.5决策树算法
Aug 29 Python
python生成九宫格图片
Nov 19 Python
Django框架使用mysql视图操作示例
May 15 Python
用pycharm开发django项目示例代码
Jun 13 Python
python修改字典键(key)的方法
Aug 05 Python
Python如何基于smtplib发不同格式的邮件
Dec 30 Python
python实现图片转换成素描和漫画格式
Aug 19 Python
python实现测试工具(二)——简单的ui测试工具
Oct 19 Python
python使用selenium爬虫知乎的方法示例
Oct 28 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
PHP利用超级全局变量$_GET来接收表单数据的实例
2016/11/05 PHP
php PDO属性设置与操作方法分析
2018/12/27 PHP
JavaScript 函数式编程的原理
2009/10/16 Javascript
文本框输入时 实现自动提示(像百度、google一样)
2012/04/05 Javascript
jQuery实现用方向键控制层的上下左右移动
2013/01/13 Javascript
jQuery插件jQuery-JSONP开发ajax调用使用注意事项
2013/11/22 Javascript
JS+CSS实现的日本门户网站经典选项卡导航效果
2015/09/27 Javascript
实例讲解jQuery EasyUI tree中state属性慎用
2016/04/01 Javascript
jQuery获取访问者IP地址的方法(基于新浪API与QQ查询接口)
2016/05/25 Javascript
jQuery实现鼠标经过像翻页和描点链接效果
2016/08/08 Javascript
jQuery ready()和onload的加载耗时分析
2016/09/08 Javascript
JavaScript创建对象的七种方式(推荐)
2017/06/26 Javascript
微信小程序中使用echarts的实现方法
2019/04/24 Javascript
AngularJS实现的鼠标拖动画矩形框示例【可兼容IE8】
2019/05/17 Javascript
Vue登录主页动态背景短视频制作
2019/09/21 Javascript
jquery+css3实现的经典弹出层效果示例
2020/05/16 jQuery
微信小程序scroll-view实现滚动到锚点左侧导航栏点餐功能(点击种类,滚动到锚点)
2020/06/11 Javascript
js+html+css实现手动轮播和自动轮播
2020/12/30 Javascript
[01:06]DOTA2隆重推出2016冬季勇士令状 内含上海特级锦标赛互动指南
2016/02/17 DOTA
Python中的异常处理学习笔记
2015/01/28 Python
用Python登录好友QQ空间点赞的示例代码
2017/11/04 Python
使用Python向C语言的链接库传递数组、结构体、指针类型的数据
2019/01/29 Python
python爬取酷狗音乐排行榜
2019/02/20 Python
Python匿名函数/排序函数/过滤函数/映射函数/递归/二分法
2019/06/05 Python
python json load json 数据后出现乱序的解决方案
2020/02/27 Python
python数据类型可变不可变知识点总结
2020/03/06 Python
佳能德国网上商店:Canon德国
2017/03/18 全球购物
打造高效课堂实施方案
2014/03/22 职场文书
研究生求职自荐书
2014/06/23 职场文书
教师党的群众路线学习心得体会
2014/11/04 职场文书
践行党的群众路线心得体会
2014/11/05 职场文书
2016年秋季新学期致辞
2015/07/30 职场文书
团队拓展训练感想
2015/08/07 职场文书
django上传文件的三种方式
2021/04/29 Python
MySQL下使用Inplace和Online方式创建索引的教程
2021/05/26 MySQL
教你用Java在个人电脑上实现微信扫码支付
2021/06/13 Java/Android