非并发程序(用于对比)

从网上下载20个国家的国旗图像:

import os
import time
import sys import requests # 导入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,下载图像
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(): #记录耗时
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()

结果:

BD BR CD CN DE EG ET FR ID IN IR JP MX NG PH PK RU TR US VN
20 flags downloaded in 32.57s

Future类

标准库中有两个名为Future的类:concurrent.futures.Future和asyncio.Future。这两个类作用相同:他们的实例都表示可能已经完成或者尚未完成的延迟计算。

一般来说,使用Executor.submit()将Future类实例化,参数为一个可调用对象,然后会为传入的可调用对象排期,并返回一个future。

future表示终将发生的事情,而确定某件事会发生的唯一方式是执行时间已经排定。因此,只有把某件事交给concurrent.futures.Excutor子类处理,才会创建Future实例。

客户端代码不应该改变future的状态,因为无法控制计算何时结束。

Future类方法

cancel():尝试去取消调用。如果调用当前正在执行,不能被取消。这个方法将返回False,否则调用将会被取消,方法将返回True

cancelled():如果调用被成功取消返回True

running():如果当前正在被执行不能被取消返回True

done():如果调用被成功取消或者完成running返回True

result(Timeout = None):拿到调用返回的结果。如果没有执行完毕就会去等待         //asyncio模块的result方法不支持设定时间

exception(timeout=None):捕获程序执行过程中的异常

add_done_callback(fn):将fn绑定到future对象上。当future对象被取消或完成运行时,fn函数将会被调用

Executor类

Executor是一个抽象类,它提供了异步执行调用的方法。它不能直接使用,但可以通过它的两个子类ThreadPoolExecutor或者ProcessPoolExecutor进行调用。

Executor.submit(fn, *args, **kwargs):函数fn(*args **kwargs)返回一个Future对象代表调用的执行。

Executor.map(func, *iterables, timeout=None, chunksize=1) :类似于map,返回一个迭代器,迭代器的__next__方法调用各个future的result方法,得到各个future的结果。

shutdown(wait=True):给executor发信号,使其释放资源,当futures完成执行时。已经shutdown再调用submit()或map()会抛出RuntimeError。使用with语句,就可以避免必须调用本函数。

ThreadPoolExecutor类

ThreadPoolExecutor类是Executor的子类,它实现的接口能在不同的线程中执行可调用对象,在内部维护一个工作线程池。

多线程并发下载国旗程序:

import os
import time
import sys
from concurrent import futures #导入模块 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/' MAX_WORKERS = 20 #设定最大线程个数 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_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: #上下文管理器,.__exit__方法会调用.shutdown(wait=True),它会在所有线程执行完毕前阻塞线程
res = executor.map(download_one, sorted(cc_list)) #类似map,见说明 return len(list(res)) def main(): #获取时间
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()

结果:

IN VN FR ID RU DE EG NG BD MX ET US CD CN BR PH IR JP TR PK
20 flags downloaded in 3.80s

使用多线程使下载时间从32s减少到3.8s。

ProcessPoolExecutor类

ProcessPoolExecutor类是Executor的子类,它实现的接口能在不同的进程中执行可调用对象,在内部维护一个工作进程池。

ThreadPoolExecutor.__init__方法需要指定max_workers参数来指定线程数量。在ProcessPoolExecutor类中,这个参数是可选的,大多数情况下默认使用os.cpu_count()函数返回的cpu数量。

将download_many变为如下:

def download_many(cc_list):
with futures.ProcessPoolExecutor() as executor:
res = executor.map(download_one, sorted(cc_list)) return len(list(res))

结果:

BD BR CD CN DE FR EG ID IN IR ET JP NG MX PH RU PK TR VN US
20 flags downloaded in 8.84s

我的计算机为4核心,它下载速度大致提升为单进程的4倍。(进程间切换需要时间)

as_completed函数

concurrent.futures.as_completed(fs, timeout=None)函数是futures模块独立的函数,它:返回一个迭代器,包含fs给出的future对象。任何future对象在该函数调用之前产生,如果调用__next__函数之后指定秒数之前结果不可用则引发超时异常。

将executor.map方法替换为executor.submit方法+futures.as_completed函数:

submit(创建并排定future)+as_completed(获取future的结果)= map                    //map弊端,如果一个调用生成结果比较耗时,则代码会阻塞;而两个函数结合即出现某个结果立即获取。

def download_many(cc_list):
cc_list = cc_list[:5]
with futures.ThreadPoolExecutor(max_workers=3) as executor: #硬编码3线程
to_do = []
for cc in sorted(cc_list):
future = executor.submit(download_one, cc) #使用submit排定可调用对象的执行时间
to_do.append(future) #存储各个future,以便后面传递给as_completed
msg = 'Scheduled for {}: {}'
print(msg.format(cc, future)) #显示国家代码和对应的future results = []
for future in futures.as_completed(to_do): #future运行结束后产出future
res = future.result() #获取future结果
msg = '{} result: {!r}'
print(msg.format(future, res)) #显示结果
results.append(res) return len(results)

wait函数

concurrent.futures.wait(fstimeout=Nonereturn_when=ALL_COMPLETED)       //返回两个具名元组集合,第一个集合叫done表示已完成或取消的future,一个叫not_done与之相反。第三个可选参数是指定函数返回时间,FIRST_COMPLETED表示任何future完成或取消;FIRST_EXCEPTION抛出异常时;ALL_COMPLETED为默认,所有future完成或取消。

异常类

CancelledError ,TimeoutError , BrokenExecutor  ,BrokenThreadPool ,BrokenProcessPool

以上来自《流畅的python》

python并发与futures模块的更多相关文章

  1. Python之concurrent.futures模块的使用

    concurrent.futures的作用:       管理并发任务池.concurrent.futures模块提供了使用工作线程或进程池运行任务的接口.线程和进程池API都是一样,所以应用只做最小 ...

  2. python之concurrent.futures模块

    一.concurrent.futures模块简介 concurrent.futures 模块提供了并发执行调用的高级接口 并发可以使用threads执行,使用ThreadPoolExecutor 或 ...

  3. 《转载》Python并发编程之线程池/进程池--concurrent.futures模块

    本文转载自Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mult ...

  4. python并发模块之concurrent.futures(二)

    python并发模块之concurrent.futures(二) 上次我们简单的了解下,模块的一些基本方法和用法,这里我们进一步对concurrent.futures做一个了解和拓展.上次的内容点这. ...

  5. Python并发复习4- concurrent.futures模块(线程池和进程池)

    Python标准库为我们提供了threading(多线程模块)和multiprocessing(多进程模块).从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提 ...

  6. Python并发编程之线程池/进程池--concurrent.futures模块

    一.关于concurrent.futures模块 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码,但是当项目达到一定的规模,频繁创建/ ...

  7. python并发模块之concurrent.futures(一)

    Python3.2开始,标准库为我们提供了concurrent.futures模块,它提供了ThreadPoolExecutor和ProcessPoolExecutor两个类,实现了对threadin ...

  8. Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)

    Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...

  9. Python之网络编程之concurrent.futures模块

    需要注意一下不能无限的开进程,不能无限的开线程最常用的就是开进程池,开线程池.其中回调函数非常重要回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去自己加 ...

随机推荐

  1. BuildPack 打包

    无需 dockerfile,使用 buildpacks 打包镜像 书接上文,聪明如你已经发现项目中没有定义 dockerfile,但我们依然能打镜像,是如何做到的呢?正如上面提到的 gradle 的 ...

  2. Blazor+Dapr+K8s微服务之开发环境调试

    1         安装Dapr开发调试环境 1.1         Dapr 完整安装模式不支持开发调试 在上一篇随笔<Blazor+Dapr+K8s微服务之服务调用>中,我们通过为每个 ...

  3. NOIP 模拟 $20\; \rm 玩具$

    题解 \(by\;zj\varphi\) 一道概率与期望好题 对于一棵树,去掉根后所有子树就是一个森林,同理,一个森林加一个根就是一棵树 设 \(f_{i,j}\) 为有 \(i\) 个点的树,高度为 ...

  4. git flow版本

    feature 分支:开发者进行功能开发的分支. develop 分支:对开发的功能进行集成的分支. release 分支:负责版本发布的分支. hotfix 分支:对线上缺陷进行修复工作的分支,热修 ...

  5. C++基于ATL工程编写ActiveX控件步骤

    参考网址: https://blog.csdn.net/whui19890911/article/details/8896554 开发环境:VS2010 开发工程:C++ATL项目 开发目的:创建Ac ...

  6. bicabo C#多线程详解(三)

    继续上一节的问题:调换两个新创建的线程启动顺序会是什么结果? using System; using System.Threading;namespace Test{    class TestThr ...

  7. 【gdal】创建GeoTiff栅格数据

    1 //定义转换参数 2 private readonly double[] d_transform = { 69.999999999999972, 0.01, 0.0, 44.99999999999 ...

  8. Flink与Strom两个框架的对比分析

    一.Flink与Storm两个框架的对比 二.Flink 的特性 1.高吞吐.低延迟.高性能 2.支持带事件的窗口(window) 操作:time.count.session.data-driven ...

  9. JOB状态与并发

    由于job每次被执行时都会创建一个新的实例, jobDetail实例时,要进行数据存储或者,特殊字段操作,需要每次schedul执行job时保留之前的数据, 那么就需要job在有状态下保持之前的数据信 ...

  10. centos7 Tomcat 多项目配置

    2021-07-30 1. Tomcat 各目录功能说明 bin :脚本文件目录,存放启动和关闭 Tomcat 的脚本文件conf:存放 Tomcat 的配置文件,server.xml 尤其重要log ...