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中的列表推导浅析
Apr 26 Python
Python随机生成一个6位的验证码代码分享
Mar 24 Python
Python中用altzone()方法处理时区的教程
May 22 Python
读写json中文ASCII乱码问题的解决方法
Nov 05 Python
numpy中索引和切片详解
Dec 15 Python
python输出100以内的质数与合数实例代码
Jul 08 Python
解决python线程卡死的问题
Feb 18 Python
Python3 log10()函数简单用法
Feb 19 Python
Python+threading模块对单个接口进行并发测试
Jun 25 Python
Django中URL的参数传递的实现
Aug 04 Python
python中使用asyncio实现异步IO实例分析
Feb 26 Python
python manim实现排序算法动画示例
Aug 14 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
B2K与车机的中波PK
2021/03/02 无线电
PHP is_dir() 判断给定文件名是否是一个目录
2010/05/10 PHP
详解PHP数组赋值方法
2015/11/07 PHP
CI框架整合smarty步骤详解
2016/05/19 PHP
thinkPHP5.0框架配置格式、加载解析与读取方法
2017/03/17 PHP
js实现日历可获得指定日期周数及星期几示例分享(js获取星期几)
2014/03/14 Javascript
new Date()问题在ie8下面的处理方法
2014/07/31 Javascript
jQuery实现强制cookie过期方法汇总
2015/05/22 Javascript
Angular懒加载机制刷新后无法回退的快速解决方法
2016/08/30 Javascript
Node.js利用Net模块实现多人命令行聊天室的方法
2016/12/23 Javascript
jQuery实现加入收藏夹功能(主流浏览器兼职)
2016/12/24 Javascript
js仿网易表单及时验证功能
2017/03/07 Javascript
jQuery插件HighCharts实现的2D堆条状图效果示例【附demo源码下载】
2017/03/14 Javascript
在一般处理程序(ashx)中弹出js提示语
2017/08/16 Javascript
分析javascript中9 个常见错误阻碍你进步
2017/09/18 Javascript
浅谈vue中慎用style的scoped属性
2017/11/28 Javascript
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
2018/11/12 Javascript
vue input实现点击按钮文字增删功能示例
2019/01/29 Javascript
微信小程序实现日历小功能
2020/11/18 Javascript
Python开发微信公众平台的方法详解【基于weixin-knife】
2017/07/08 Python
基于Django与ajax之间的json传输方法
2018/05/29 Python
Python numpy实现二维数组和一维数组拼接的方法
2018/06/05 Python
python 一篇文章搞懂装饰器所有用法(建议收藏)
2019/08/23 Python
基于python的BP神经网络及异或实现过程解析
2019/09/30 Python
Python如何使用字符打印照片
2020/01/03 Python
Python编程快速上手——疯狂填词程序实现方法分析
2020/02/29 Python
html5+css3气泡组件的实现
2014/11/21 HTML / CSS
奇怪的鱼:Weird Fish
2018/03/18 全球购物
事业单位请假制度
2014/01/13 职场文书
领导班子三严三实对照检查材料
2014/09/25 职场文书
社区环境卫生倡议书
2015/04/29 职场文书
横空出世观后感
2015/06/09 职场文书
庆祝教师节主题班会
2015/08/17 职场文书
2019年预备党员的思想汇报:加深对党的认知
2019/09/25 职场文书
3050和2060哪个好 性能差多少 差距有多大 谁更有性价比
2022/06/17 数码科技
MySQL中TIMESTAMP类型返回日期时间数据中带有T的解决
2022/12/24 MySQL