本章讨论python3.2引入的concurrent.futures模块。future是中文名叫期物。期物是一种对象,表示异步执行的操作

在很多任务中,特别是处理网络I/O。需要使用并发,因为网络有很高的延迟。所以为了不浪费CPU周期去等待,最好在收到网络响应之前做些其他的事。

首先来看下并发和非并发的两个脚本,来对比下各自的运行效率。在这个程序中,我们通过脚本去网站下载各个国家的国旗。网址是http://flupy.org/data/flags/cn/cn.gif

这里http://flupy.org/是基本URL,后面接/data/flags/国家名称/国家名称.gif

首先来看下顺序下载的代码:

country=('CN IN US ID BR PK NG BD JP MX PH VN ET EG DE IR TR CD FR').split()

BASE_URL='http://flupy.org/data/flags'

DEST_URL='downloads/'

def save_flag(img,filename):

path=os.path.join(DEST_URL,filename)

with open(path,'wb') as f:

f.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)

sys.stdout.flush()

def download_many(cc_list):

for cc in sorted(cc_list):

img=get_flag(cc)

show(cc)

save_flag(img,cc.lower()+'.gif')

return len(cc_list)

def main(download_many):

t1=time.time()

count=download_many(country)

elapsed=time.time()-t1

msg='\n{} flags downloaded in {:.2f}s'

print(msg.format(count,elapsed))

if __name__=="__main__":

main(download_many)

代码中通过requests对图片进行下载并且保存到downloads文件夹下面。并且在main中统计代码运行的时间。运行结果如下:

/usr/bin/python3.6 /home/zhf/py_prj/function_test/test.py

BD

BR

CD

CN

DE

EG

ET

FR

ID

IN

IR

JP

MX

NG

PH

PK

TR

US

VN

19 flags downloaded in 15.29s

下载了19个图片总共花费15.29秒。接下来我们用concurrent.futures模块来对代码进行改造。在这里添加download_one和download_many_futures两个函数

在ThreadPoolExecutor中设置最大运行的线程max_workers为3个

executor.submit中传入单个的回调函数和参数

future.result()返回的是每个线程运行完后的结果,在这里就是download_one的返回值

futures.as_completed是一个迭代器,在期物运行结束后产出期物

def download_one(cc):

image=get_flag(cc)

show(cc)

save_flag(image,cc.lower()+'.gif')

return cc

def download_many_futures(cc_list):

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='Scheduled 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)

来看下运行的结果:总共耗时6.72秒,比之前的顺序下载节省了一半的时间

Scheduled for BD:<Future at 0x7f51f9c7e908 state=running>

Scheduled for BR:<Future at 0x7f51f9c7ef60 state=running>

Scheduled for CD:<Future at 0x7f51f8a20518 state=running>

Scheduled for CN:<Future at 0x7f51f8a20ef0 state=pending>

Scheduled for DE:<Future at 0x7f51f8a20f60 state=pending>

Scheduled for EG:<Future at 0x7f51f8a2c048 state=pending>

Scheduled for ET:<Future at 0x7f51f8a2c0f0 state=pending>

Scheduled for FR:<Future at 0x7f51f8a2c198 state=pending>

Scheduled for ID:<Future at 0x7f51f8a2c278 state=pending>

Scheduled for IN:<Future at 0x7f51f8a2c358 state=pending>

Scheduled for IR:<Future at 0x7f51f8a2c438 state=pending>

Scheduled for JP:<Future at 0x7f51f8a2c518 state=pending>

Scheduled for MX:<Future at 0x7f51f8a2c5f8 state=pending>

Scheduled for NG:<Future at 0x7f51f8a2c6d8 state=pending>

Scheduled for PH:<Future at 0x7f51f8a2c7b8 state=pending>

Scheduled for PK:<Future at 0x7f51f8a2c898 state=pending>

Scheduled for TR:<Future at 0x7f51f8a2c978 state=pending>

Scheduled for US:<Future at 0x7f51f8a2ca58 state=pending>

Scheduled for VN:<Future at 0x7f51f8a2cb38 state=pending>

BD

<Future at 0x7f51f9c7e908 state=finished returned str> result:'BD'

BR

<Future at 0x7f51f9c7ef60 state=finished returned str> result:'BR'

CD

<Future at 0x7f51f8a20518 state=finished returned str> result:'CD'

CN

<Future at 0x7f51f8a20ef0 state=finished returned str> result:'CN'

DE

<Future at 0x7f51f8a20f60 state=finished returned str> result:'DE'

EG

<Future at 0x7f51f8a2c048 state=finished returned str> result:'EG'

FR

<Future at 0x7f51f8a2c198 state=finished returned str> result:'FR'

ID

ET

<Future at 0x7f51f8a2c0f0 state=finished returned str> result:'ET'

<Future at 0x7f51f8a2c278 state=finished returned str> result:'ID'

IN

<Future at 0x7f51f8a2c358 state=finished returned str> result:'IN'

JP

<Future at 0x7f51f8a2c518 state=finished returned str> result:'JP'

IR

<Future at 0x7f51f8a2c438 state=finished returned str> result:'IR'

NG

<Future at 0x7f51f8a2c6d8 state=finished returned str> result:'NG'

MX

<Future at 0x7f51f8a2c5f8 state=finished returned str> result:'MX'

PK

<Future at 0x7f51f8a2c898 state=finished returned str> result:'PK'

PH

<Future at 0x7f51f8a2c7b8 state=finished returned str> result:'PH'

TR

<Future at 0x7f51f8a2c978 state=finished returned str> result:'TR'

US

<Future at 0x7f51f8a2ca58 state=finished returned str> result:'US'

VN

<Future at 0x7f51f8a2cb38 state=finished returned str> result:'VN'

19 flags downloaded in 6.72s。如果我们将max_workers设置为更大的值,比如设置为10, 得到的运行时间将会更快。通过运行的结果最快的时候达到1.9秒。

我们都知道python有全局解释器锁GIL,一次只允许使用一个线程执行python字节码。因此一个Python进程通过不能同时使用多个CPU核。关于GIL的解释可以参考http://cenalulu.github.io/python/gil-in-python/这篇帖子。

那么如果我们有多个CPU核(os.cpu_count可以查看有多少个CPU核),我们该怎么利用呢。这里就要用到futures.ProcessPoolExecutor,ProcessPoolExecutor将工作分配给多个python进程处理。因此如果需要做CPU密集型处理,使用这个模块能够绕开GIL,利用所有可用的CPU核。我们将代码修改为ProcessPoolExecutor来执行看下结果

19 flags downloaded in 4.92s。这个比设置

futures.ThreadPoolExecutor(max_workers=10)的时候还要慢一些。这是由于我的电脑只有4个CPU核,因此限制只能4个并发下载。但是线程版本使用的是10个线程。在用线程运行的时候,所有阻塞型的I/O函数都会释放GIL,允许其他线程运行。time.sleep()也会释放GIL,因此尽管有GIL,python的线程还是能发挥作用。

在一章中我们将介绍asynchio包处理并发

流畅python学习笔记:第十七章:并发处理二的更多相关文章

  1. 流畅python学习笔记:第十七章:并发处理

    第十七章:并发处理 本章主要讨论Python3引入的concurrent.futures模块.在python2.7中需要用pip install futures来安装.concurrent.futur ...

  2. #Python学习笔记:1-3章 (基于《python编程,从入门到实践)

    第1-3章 这个文档是记录我学习python时一些学习笔记以及一些想法也可以称作复习笔记 第一章:起步这一章主要是从第一个"hello world"程序到python环境的搭建与配 ...

  3. [Python学习笔记][第七章Python文件操作]

    2016/1/30学习内容 第七章 Python文件操作 文本文件 文本文件存储的是常规字符串,通常每行以换行符'\n'结尾. 二进制文件 二进制文件把对象内容以字节串(bytes)进行存储,无法用笔 ...

  4. [Python学习笔记][第五章Python函数设计与使用]

    2016/1/29学习内容 第四章 Python函数设计与使用 之前的几页忘记保存了 很伤心 变量作用域 -一个变量已在函数外定义,如果在函数内需要修改这个变量的值,并将这个赋值结果反映到函数之外,可 ...

  5. [Python学习笔记][第四章Python字符串]

    2016/1/28学习内容 第四章 Python字符串与正则表达式之字符串 编码规则 UTF-8 以1个字节表示英语字符(兼容ASCII),以3个字节表示中文及其他语言,UTF-8对全世界所有国家需要 ...

  6. [汇编学习笔记][第十七章使用BIOS进行键盘输入和磁盘读写

    第十七章 使用BIOS进行键盘输入和磁盘读写 17.1 int 9 中断例程对键盘输入的处理 17.2 int 16 读取键盘缓存区 mov ah,0 int 16h 结果:(ah)=扫描码,(al) ...

  7. 流畅的python学习笔记:第二章

    第二章开始介绍了列表这种数据结构,这个在python是经常用到的结构 列表的推导,将一个字符串编程一个列表,有下面的2种方法.其中第二种方法更简洁.可读性也比第一种要好 str='abc' strin ...

  8. 流畅的python学习笔记:第一章

    这一章中作者简要的介绍了python数据模型,主要是python的一些特殊方法.比如__len__, __getitem__. 并用一个纸牌的程序来讲解了这些方法 首先介绍下Tuple和nametup ...

  9. 流畅python学习笔记:第十一章:抽象基类

    __getitem__实现可迭代对象.要将一个对象变成一个可迭代的对象,通常都要实现__iter__.但是如果没有__iter__的话,实现了__getitem__也可以实现迭代.我们还是用第一章扑克 ...

随机推荐

  1. GCJ——Minimum Scalar Product(2008 Round1 AA)

    题意: 给定两组各n个数,可任意调整同一组数之中数字的顺序,求 sum xi*yi i=1..n的最小值. Small: n<=8 abs xy,yi<=1000 Large: n< ...

  2. springboot集成PageHelper,支持springboot2.0以上版本

    第一步:pom文件还是需要引入依赖 <!--mybatis的分页插件--> <dependency> <groupId>com.github.pagehelper& ...

  3. [置顶] pycurl检测网站性能,pycurl.*_TIME时间问题

    今天使用python+pycurl来检测网站性能,使用curl_obj.getinfo(pycurl.*_TIME)来获取各个阶段运行时间 total_time = curl_obj.getinfo( ...

  4. ArcGIS教程:面积制表

    摘要 计算两个数据集之间交叉制表的区域并输出表. 插图 使用方法 · 区域定义为输入中具有同样值的全部区.各区无需相连. 栅格和要素数据集都可用于区域输入. · 假设区域输入和类输入均为具有同样分辨率 ...

  5. Java实现链表结构的具体代码

    一.数据准备 1. 定义节点 2.   定义链表 1.数据部分 2.节点部分 class DATA //数据节点类型 { String key; String name; int age; } cla ...

  6. apue学习笔记(第十二章 线程控制)

    本章将讲解控制线程行为方面的详细内容,而前面的章节中使用的都是它们的默认行为 线程属性 pthread接口允许我们通过设置每个对象关联的不同属性来细调线程和同步对象的行为.管理这些属性的函数都遵循相同 ...

  7. 工厂方法模式之C++实现

    说明:本文仅供学习交流,转载请标明出处.欢迎转载. 工厂方法模式与简单工厂模式的差别在于:在简单工厂模式中.全部的产品都是有一个工厂创造,这样使得工厂承担了太大的造产品的压力,工厂内部必须考虑所以的产 ...

  8. ICON小工具如何使用

    对于ICON这个小资源,我们可以手动绘制.选择bmp图形的大小,尤其是旁边的选择工具(矩形或者弧形),我们可以通过选择工具挪动我们手动绘制的图标,其实图标只有中间那部分有用,其他没有用.还有最右边色拾 ...

  9. java将一个或者多个空格进行分割

    public static void main(String[] args) { String s = "GET /index.html HTTP/1.1";//字符串s由“GET ...

  10. 详细解析用Squid实现反向代理的方法

    代理服务器是使 用非常普遍的一种将局域网主机联入互联网的一种方式,使用代理上网可以节约紧缺的IP地址资源,而且可以阻断外部主机对内部主机的访问,使内部网主机免受 外部网主机的攻击.但是,如果想让互联网 ...