Python asyncio库的学习和使用
因为要找工作,把之前自己搞的爬虫整理一下,没有项目经验真蛋疼,只能做这种水的不行的东西。。。T T,希望找工作能有好结果。
之前爬虫使用的是requests+多线程/多进程,后来随着前几天的深入了解,才发现,对于爬虫来说,真正的瓶颈并不是CPU的处理速度,而是对于网页抓取时候的往返时间,因为如果采用requests+多线程/多进程,他本身是阻塞式的编程,所以时间都花费在了等待网页结果的返回和对爬取到的数据的写入上面。而如果采用非阻塞编程,那么就没有这个困扰。这边首先要理解一下阻塞和非阻塞的区别
1.阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,CPU不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。
2.对于非阻塞则不会挂起,直接执行接下去的程序,返回结果后再回来处理返回值。
python 3.4开始就支持异步IO编程,提供了asyncio库,但是3.4中采用的是@asyncio.coroutine和
yield from这和原来的generator关键字yield不好区分,在3.5中,采用了async(表示携程)和await关键字,这样就好区分多了。
写这篇博客的目的在于,网上看了一堆资料,自己还是没有理解如何进行asyncio的编程,用到自己代码里时候,还是有各种错误,因此打算看一下asyncio的官方手册并好好梳理一下,希望时间的来的及~
https://docs.python.org/3/library/asyncio.html
这边用一个简单的官方例子来说明async和await的执行顺序。
import asyncio async def compute(x, y):
print("Compute %s + %s ..." % (x, y))
await asyncio.sleep(1.0)
return x + y async def print_sum(x, y):
result = await compute(x, y)
print("%s + %s = %s" % (x, y, result)) loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
如果不使用async和await,这个程序的运行顺序也很好理解
1.print_sum(1,2)将参数1,2传递给函数print_sum
2.先执行第一句,result = compute(x,y),
3.将1,2传递给compute函数,compute函数收到参数
4.先执行 print("Compute %s + %s ..." % (x, y))打印出Compute 1 + 2 ...
5.执行sleep(1.0)程序挂起一秒
6.返回1+2的值3
7.print_sum的result收到返回值3,执行print("%s + %s = %s" % (x, y, result)),打印出1 + 2 = 3
8.程序结束
如果采用异步的话,他执行顺序是怎么样的呢?
下图是他官方文档的说明:
要理解上图,首先我们要理解Event_loop,feture,task等概念,详细的可以参考官方文档或者http://my.oschina.net/lionets/blog/499803
首先get_event_loop()方法让我们得到一个消息环,event loop对象包括两部分:event和loop,event负责I/O时间通知二loop负责循环处理I/O通知并在就绪时调用回调。
event loop 内部维护着两个容器:_ready
和 _scheduled
。类型分别是 deque
和 list
。_ready 代表已经可以执行,_scheduled 代表计划执行。_scheduled 中的 handle 是可以 cancel 的。
要理解task,首先需要理解future对象(via http://my.oschina.net/lionets/blog/499803)
class asyncio.Future(*, loop=None)
是对一个可调用对象的异步执行控制或者说代理的封装。因此具有如下方法:
cancel()
cancelled()
done()
result()
exception()
add_done_callback(fn)
remove_done_callback(fn)
set_result(result)
set_exception(exception)
注意 Future 并不包含可执行对象的本体,他只保存状态、结果、额外的回调函数这些东西。这也是上面称之为代理的原因。因为实际的调用过程是在 event loop 里发生的,event loop 负责在异步执行完成后向 future 对象写入 result 或 exception。这是异步任务的基本逻辑。
future 实例有三种状态:
- PENDING
- CANCELLED
- FINISHED
初始状态为 PENDING,当调用 cancel()
方法时会立即进入 CANCELLED 状态并 schedule callbacks。当被调用 set_result()
时会进入 FINISHED 状态,并 schedule callbacks。当然两种情况下传入 callback 的参数会不同。
Task是 Future
的子类。因为 Future 没有保存其相关可执行对象的信息,我们 schedule the execution of a coroutine 这件事一般是通过 Task 对象来做的。 Task 提供了一种机制用于当 future 完成时唤醒父级协程。即为当 await future 时,task 对象会将此 future 保存到 _fut_waiter
对象中,并为其添加一个名为_wake_up()
的回调。
继续分析上面这段代码,首先async def创建一个coroutine object,get_event_loop创建一个消息环,接下去,run_until_complete()接受coroutine object对象,通过隐式转换(ensure_future())将其包装成一个future,这边是一个task对象(内部实现机制我其实也并不是非常确定,大致是这样,比如如何从future转换成task??)
1.get_event_loop()创建消息环
2.print_sum返回一个携程对象(coroutine object)
3.print_sum(1,2)接受参数
4.result = await compute(x,y),此时程序挂起,直接调用compute(1,2)
5.打印出Compute 1 + 2 ...
6.执行sleep(1.0) 由于使用await,所以sleep(1.0)被挂起,直接执行下一句
7.这边由于只有一个task,即print_sum(1,2),因此没有其他task可以执行,所以result等待compute(x,y)的返回值,等待1秒以后,返回1+2的值3
然后执行print("%s + %s = %s" % (x, y, result))
其实上面的程序可能还不能很明白async def和 await的调用过程到底是怎么样,稍微修改一下上面的程序就能更加明白他的执行过程了。
import asyncio async def compute(x, y):
print("Compute %s + %s ..." % (x, y))
await asyncio.sleep(10.0)
return x + y async def print_sum(x, y):
result = await compute(x, y)
print("%s + %s = %s" % (x, y, result)) loop = asyncio.get_event_loop()
tasks = [print_sum(1,2),print_sum(3,4)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
把代码修改成这样,其他不变,多了一个task,为了更加直观,我把sleep(1.0)改成了sleep(10.0)
此时程序的执行结果是
Compute 3 + 4 ...
Compute 1 + 2 ...
暂停10秒左右
3 + 4 = 7
1 + 2 = 3
非常需要注意的就是这边暂停的时间,是10秒左右,不是20秒,所以他执行顺序是对于task
先执行print_sum(3,4)(这边tasks额执行顺序我不是非常理解,我多加了很多task测验,发现都是从第二个开始执行,然后最后执行第一个task,没懂是为什么)将3,4扔给compute,在compute中,sleep(10.0)挂起,此时不等待sleep(10.0)执行完毕,直接执行tasks中的另一个task,print_sum(1,2),将1,2扔给compute,在compute中,sleep(10.0)挂起,由于已经没有其他的task,所以等待第一个sleep(10.0)执行完毕以后返回3+4的结果为7,执行result = await compute(3, 4)后面的程序,即打印出3 + 4 = 7执行完毕以后,第二个sleep(10.0)也差不多返回了,因此回到原来挂起的result = await compute(1, 2),打印出1 + 2 = 3.所有task执行完毕,loop complete并且close。
这样整个async def 和await的执行流程就比较清楚了。
Python asyncio库的学习和使用的更多相关文章
- Python Pandas库的学习(一)
今天我们来学习一下Pandas库,前面我们讲了Numpy库的学习 接下来我们学习一下比较重要的库Pandas库,这个库比Numpy库还重要 Pandas库是在Numpy库上进行了封装,相当于高级Num ...
- python 标准库基础学习之开发工具部分1学习
#2个标准库模块放一起学习,这样减少占用地方和空间#标准库之compileall字节编译源文件import compileall,re,sys#作用是查找到python文件,并把它们编译成字节码表示, ...
- Python Pandas库的学习(二)
今天我们继续讲下Python中一款数据分析很好的库.Pandas的学习 接着上回讲到的,如果有人听不懂,麻烦去翻阅一下我前面讲到的Pandas学习(一) 如果我们在数据中,想去3,4,5这几行数据,那 ...
- 这十个Python常用库,学习Python的你必须要知道!
想知道Python取得如此巨大成功的原因吗?只要看看Python提供的大量库就知道了 包括原生库和第三方库.不过,有这么多Python库,有些库得不到应有的关注也就不足为奇了.此外,只在一个领域里的工 ...
- 这十个Python常用库?学习Python的你必须要知道!
想知道Python取得如此巨大成功的原因吗?只要看看Python提供的大量库就知道了 ,包括原生库和第三方库.不过,有这么多Python库,有些库得不到应有的关注也就不足为奇了.此外,只在一个领域里的 ...
- Python Pandas库的学习(三)
今天我们来继续讲解Python中的Pandas库的基本用法 那么我们如何使用pandas对数据进行排序操作呢? food.sort_values("Sodium_(mg)",inp ...
- Python 标准库一览(Python进阶学习)
转自:http://blog.csdn.net/jurbo/article/details/52334345 写这个的起因是,还是因为在做Python challenge的时候,有的时候想解决问题,连 ...
- python requests库学习
Python 第三方 http 库-Requests 学习 安装 Requests 1.通过pip安装 $ pip install requests 2.或者,下载代码后安装: $ git clone ...
- python requests库学习笔记(上)
尊重博客园原创精神,请勿转载! requests库官方使用手册地址:http://www.python-requests.org/en/master/:中文使用手册地址:http://cn.pytho ...
随机推荐
- timer控件、三级联动
timer控件: 实现时间日期自增长: using System; using System.Collections.Generic; using System.ComponentModel; usi ...
- linux chomd 学习
chomd -R 777 directory_name :递归地给directory目录下所有文件和子目录的属主分配读的权限 ------2016-10-31 -- source: Linux chm ...
- 学习mongo系列(七)aggregate() ,$group() 管道
aggregate()聚合,主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果.有点类似sql语句中的 count(*) 接上边的数据库: > db.user.aggregate ...
- reg.test is not a function 报错
正则中 比如 var reg = "/^[0-9]$/" 会报 reg.test is not a function 如果 var reg = /^[0-9]$/ 就不会有错 因为 ...
- js高级程序设计(六)面向对象
ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性或方法都有一个名字,而每个名字都映射到一个值.正 ...
- 0505 Scrum 项目1.0
应用NABCD模型,分析你们初步选定的项目,充分说明你们选题的理由. 录制为演说视频,上传到视频网站,并把链接发到团队博客上. 团队项目选题 一个售书网站(O2O) NABCD 模型 1) N (N ...
- javascript的一些基础
当复制的两个变量的地址不同时他们是不相等的如下代码所示 function getFunction(value){ return function(value){ return value; } } v ...
- jquery.uploadify 动态传递参数
最近 项目中使用到 uplaodify 来实现上传文件的功能.在传输动态参数的时候,遇到了问题! 使用官网提供的 settings 方法 官方例子function changeBtnText() { ...
- 介绍开源的.net通信框架NetworkComms框架之九 合并DLL
原文网址: http://www.cnblogs.com/csdev Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是 ...
- .htaccess根据IP地址限制访问
屏蔽IP地址 屏蔽IP地址有时是非常必要的,比如对于一个外贸公司网站,来自国内的访问是不会带来任何经济效益的,而且还占用服务器资源,造成访问延迟等问题. 如果要屏蔽某一特定IP可以使用: order ...