前面介绍了asyncio的用法。下面我们来看下如何用协程的方式来实现之前的旋转指针的方法

@asyncio.coroutine

def spin(msg):

write,flush=sys.stdout.write,sys.stdout.flush

for char in itertools.cycle('|/-\\'):

status=char+''+msg

write(status)

flush()

write('\x08'*len(status))

try:

yield from asyncio.sleep(0.1)

except asyncio.CancelledError:

break

write(''*len(status)+'\0x8'*len(status))

@asyncio.coroutine

def slow_function():

yield from asyncio.sleep(3)

return 42

@asyncio.coroutine

def supervisor():

spinner=asyncio.ensure_future(spin('thinking'))

#    spinner=asyncio.async(spin('thinking'))

print('spinner object:',spinner)

result=yield from slow_function()

spinner.cancel()

return result

if __name__=="__main__":

start=time.time()

loop=asyncio.get_event_loop()

result=loop.run_until_complete(supervisor())

loop.close()

print('Answer:',result)

end=time.time()

print("Total time:{}".format(end-start))

运行的结果和之前用多线程是一样的。我们来看下运行的具体原理

1 asyncio.get_event_loop和 loop.run_until_complete(supervisor())

创建主循环,并传入任务supervisor

2 在supervisor中首先通过asyncio.async(spin('thinking'))将spin函数添加如Task。方法也可以是通过spinner=asyncio.ensure_future(spin('thinking'))。在asyncio中Task对象和threading.Thread的用途是一样的。Task对象用于驱动协程,Thread对象用于调用可调用的对象,Task对象不用自己实例化,而是通过把协程传给asyncio.async函数或者是asyncio.ensure_future,或者是loop.create_task

3 在spin函数中,执行在终端输出旋转指针。并通过asyncio.sleep(0.1)的方式让出控制权,会到主循环

4 此时来到supervisor函数。此时进入slow_function,在slow_function中asyncio.sleep(3)进行休眠,并在休眠期把控制权交给主循环。此时主循环会将控制权又交给spin函数。

5 3秒休眠结束后,返回42,并通过spinner.cancel函数取消spin的task,取消后会在协程当前暂停的yield出抛出asyncio.CancelledError异常。至此整个程序运行完毕。

我们继续来看下用asyncio来实现图片下载的程序

DEST_URL='downloads/'

BASE_URL1='http://seopic.699pic.com/photo/40011'

down_list=('8840.jpg_wh1200','7347.jpg_wh1200','6876.jpg_wh1200','6876.jpg_wh1200')

def save_flag(img,filename):

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

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

f.write(img)

@asyncio.coroutine

def get_flag(cc):

url='{}/{cc}.jpg'.format(BASE_URL1,cc=cc)

print(url)

resp = yield from aiohttp.ClientSession().get(url)

print (resp.status)

image = yield from resp.read()

return image

def show(text):

print(text)

sys.stdout.flush()

@asyncio.coroutine

def download_one(cc):

image = yield from get_flag(cc)

show(cc)

save_flag(image, cc + '.jpg')

return cc

def download_many(cc_list):

loop=asyncio.get_event_loop()

to_do=[download_one(cc) for cc in sorted(cc_list)]

wait_coro=asyncio.wait(to_do)

res,_=loop.run_until_complete(wait_coro)

loop.close()

return len(res)

def main(download_many):

t1=time.time()

count=download_many(down_list)

elapsed=time.time()-t1

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

print(msg.format(count,elapsed))

if __name__=="__main__":

main(download_many)

在这里我们用yield from aiohttp.ClientSession().get(url)

代替了request函数。因为request函数是个IO阻塞型的函数。注意aiohttp必须要安装才能使用。书上写的是用aiohttp.request(‘GET’,url)方法,但是我在实际使用的时候发现无法下载,提示如下错误:

client_session: <aiohttp.client.ClientSession object at 0x7fac75231f28>

Unclosed client session

client_session: <aiohttp.client.ClientSession object at 0x7fac75231dd8>

Unclosed client session

client_session: <aiohttp.client.ClientSession object at 0x7fac75231eb8>

Unclosed client session

client_session: <aiohttp.client.ClientSession object at 0x7fac75231f28>

Task exception was never retrieved

future: <Task finished coro=<download_one() done, defined at /home/zhf/py_prj/function_test/asy_try.py:51> exception=TypeError("'_SessionRequestContextManager' object is not iterable",)>

Traceback (most recent call last):

File "/home/zhf/py_prj/function_test/asy_try.py", line 53, in download_one

image = yield from get_flag(cc)

File "/home/zhf/py_prj/function_test/asy_try.py", line 39, in get_flag

resp=yield from aiohttp.request('GET',url)

TypeError: '_SessionRequestContextManager' object is not iterable

Task exception was never retrieved

future: <Task finished coro=<download_one() done, defined at /home/zhf/py_prj/function_test/asy_try.py:51> exception=TypeError("'_SessionRequestContextManager' object is not iterable",)>

在网上搜了下,推荐使用aiohttp.ClientSession().get进行下载。这个函数能保证相关的TCP资源能够得到释放,比如TCP链接

在这里download_one和get_flag都用到了协程, 意味着必须像协程那样驱动,这样才能把控制权交还给时间循环

asyncio.wait分别把各个协程包装进一个task对象,最后的结果是,wait处理的所有对象都通过某种方式变成Future类的实例,wait是协程函数,因此返回的是一个协程或生成器对象。为了驱动协程,我们把协程传给run_until_completed方法。

运行时间:

5 flags downloaded in 0.34s

下面继续来优化下这个下载程序。

首先将代码改动下,在保存图片的时候将大小放大1000倍

def save_flag(img,filename):

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

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

f.write(img*1000)

5 flags downloaded in 13.46s

下载的图片大小如下所示:一张图片493M

那么耗时的时间呢.总共耗费了13.46秒的时间。速率降低了40多倍

5 flags downloaded in 13.46s

原因是什么呢。原因就在与save_flag是一个阻塞性的函数(f.write)。save_flag函数阻塞了客户代码与asyncio事件循环共用的唯一线程。因此保存文件的时候,整个程序都会冻结。那么解决办法就是使用run_in_executor方法。download_one代码修改如下:

@asyncio.coroutine

def download_one(cc):

image = yield from get_flag(cc)

show(cc)

loop=asyncio.get_event_loop()

loop.run_in_executor(None,save_flag,image,cc+'.jpg')

return cc

修改之后总共耗时1.5秒

5 flags downloaded in 1.50s

流畅python学习笔记第十八章:使用asyncio包处理并发(二)的更多相关文章

  1. 流畅python学习笔记第十八章:使用asyncio包处理并发(一)

    首先是线程与协程的对比.在文中作者通过一个实例分别采用线程实现和asynchio包实现来比较两者的差别.在多线程的样例中,会用到join的方法,下面来介绍下join方法的使用. 知识点一:当一个进程启 ...

  2. 流畅python学习笔记第十八章:使用asyncio编写服务器

    在这一章中,将使用asyncio写一个TCP服务器.这个服务器的作用是通过规范名称查找Unicode字符,来看下代码: import asyncio from charfinder import Un ...

  3. 流畅的python第十八章使用asyncio包处理并发

    对比一个简单的多线程程序和对应的 asyncio 版,说明多线程和异步任务之间的关系asyncio.Future 类与 concurrent.futures.Future 类之间的区别摒弃线程或进程, ...

  4. Python学习笔记(十四)

    Python学习笔记(十四): Json and Pickle模块 shelve模块 1. Json and Pickle模块 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不 ...

  5. Python学习笔记(十)

    Python学习笔记(十): 装饰器的应用 列表生成式 生成器 迭代器 模块:time,random 1. 装饰器的应用-登陆练习 login_status = False # 定义登陆状态 def ...

  6. Python学习笔记(十五):类基础

    以Mark Lutz著的<Python学习手册>为教程,每天花1个小时左右时间学习,争取两周完成. --- 写在前面的话 2013-7-24 23:59 学习笔记 1,Python中的大多 ...

  7. Python学习笔记(十四):模块高级

    以Mark Lutz著的<Python学习手册>为教程,每天花1个小时左右时间学习,争取两周完成. --- 写在前面的话 2013-7-23 21:30 学习笔记 1,包导入是把计算机上的 ...

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

    本章讨论python3.2引入的concurrent.futures模块.future是中文名叫期物.期物是一种对象,表示异步执行的操作 在很多任务中,特别是处理网络I/O.需要使用并发,因为网络有很 ...

  9. 流畅python学习笔记:第十五章:上下文管理器

    在开始本章之前,我们首先来谈谈try-excep..final模块.在Python中,进行异常保护的最多就是用try..except..final.首先来看下下面的代码.进行一个简单的除法运算.为了防 ...

随机推荐

  1. Linux网络协议栈之数据包处理过程

    http://blog.csdn.net/cheng_fangang/article/details/8966242

  2. Failed to read artifact descriptor for avalon-framework:avalon-framewor

    在工程中,遇到了这个问题,百度了好久并没有满意的解决方案. 网上有一种办法是: 一.修改.m2/repository/avalon-framework/avalon-framework-api/里所有 ...

  3. Json格式化工具 JsonViewer下载

    免安装版,分享链接永久有效~! 云盘下载地址: http://cloud.suning.com/cloud-web/share/link.htm?sk=401f784782751055ddc21cdb ...

  4. 2016.7.12 在navicat中用sql语句建表

    参考资料: http://jingyan.baidu.com/article/f0e83a25a8c4b022e5910116.html 即新建query,然后run. (1)点击要新建表的位置,选择 ...

  5. c#课程设计---猜猜看游戏

    1:游戏要求 1. 随机显示 一个名字 与 若干张相片(如3张).选择正确的相片. 2. 记录老师对每一个学生的认识概率P.并依据认识概率,确定"猜猜看"游戏中学生出现的频率. 认 ...

  6. Arduino MEGA 2560找不到驱动怎么办

    刚买了Arduino MEGA 2560(比Arduino UNO稍微高级一点的板子),按照视频一步一步操作(似乎插板子也不太一样,不管他,能插上去就完事了),但是到了代码烧录的时候,点击Tools- ...

  7. 【Python】向函数传递列表

    向函数传递列表 在实际使用中你会发现,向函数传递列表是比较实用的,这种列表可能包含名字.数字.可能更复杂的对象(字典) 假设向一个函数传递一堆水果,我们说出我们喜欢所有的水果 def Obj(frui ...

  8. EasyUI这个框架用了好久了,总结一下遇到的问题和解决方法

    1. jQuery EasyUI动态添加控件或者ajax加载页面后不能自动渲染问题的解决方法: 我们之所以在页面中,只要书写相应easyui的class,Easyui就能成功渲染页面,这是因为解析器在 ...

  9. HttpClient 模拟登录网易微博

          实现核心:代码执行流程,根据抓包工具,模拟浏览器请求步骤走       private static void testLogin()       {             try    ...

  10. SpringBoot学习之启动方式

    1.通过@SpringBootAppliction注解类启动 启动方法:找到注解类->鼠标右键->run as-> java application. 2 通过maven启动Spri ...