Python学习--22 异步I/O
在同步IO中,线程启动一个IO操作然后就立即进入等待状态,直到IO操作完成后才醒来继续执行。而异步IO方式中,线程发送一个IO请求到内核,然后继续处理其他的事情,内核完成IO请求后,将会通知线程IO操作完成了。
如果IO请求需要大量时间执行的话,异步IO方式可以显著提高效率,因为在线程等待的这段时间内,CPU将会调度其他线程进行执行,如果没有其他线程需要执行的话,这段时间将会浪费掉。
协程
协程(Coroutine),又称微线程。
我们平常使用的函数又称子程序,是层级调用的,即A函数调用B,B函数又调用C,那么需要等C执行完毕返回,然后B程序执行完毕返回,最后A执行完毕。
协程看上去也是子程序,但是执行顺序和子程序不同:协程执行过程可以中断,同样是A函数调用B,但B可以执行一部分继续去执行A,然后继续执行B未执行完的部分。
协程看起来很像多线程。但协程最大的优势是极高的执行效率。因为线程需要互相切换,切换需要开销。且线程直接共享变量需要使用锁机制,因为协程只有一个线程,不存在同时写变量冲突。
Python对协程的支持是通过generator实现的。
在generator中,我们不但可以通过for循环来迭代,还可以不断调用next()
函数获取由yield
语句返回的下一个值。
但是Python的yield
不但可以返回一个值,它还可以接收调用者发出的参数。下面是一个典型的生产者-消费者
模型:
# coding: utf-8
def consumer():
r = ''
while True:
n = yield r
print('[Consumer] Consuming %s' % n)
r = '200 OK'
def produce(c):
c.send(None) #启动生成器
i = 0
while i < 5:
i = i + 1
print('[Produce] Start produce %s' % i)
r = c.send(i)
print('[Produce] Consumer return %s' % r)
c = consumer() #生成器
produce(c)
输出:
[Produce] Start produce 1
[Consumer] Consuming 1
[Produce] Consumer return 200 OK
[Produce] Start produce 2
[Consumer] Consuming 2
[Produce] Consumer return 200 OK
[Produce] Start produce 3
[Consumer] Consuming 3
[Produce] Consumer return 200 OK
[Produce] Start produce 4
[Consumer] Consuming 4
[Produce] Consumer return 200 OK
[Produce] Start produce 5
[Consumer] Consuming 5
[Produce] Consumer return 200 OK
执行顺序:
1、发送None启动生成器consumer(),运行yield r,返回'',程序中断;
2、第2次发送1,从上次运行结束的地方开始,先通过n = yield r接收到1,继续运行打印语句,再次运行到yield r,返回'200 OK';
3、第3次发送2,从上次运行结束的地方开始,先通过n = yield r接收到2,继续运行打印语句,再次运行到yield r,返回'200 OK';
4、...
大家可以使用 Intellij IDEA调试功能 进行单步运行观察执行流程。
整个流程由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
asyncio
asyncio
是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。使用asyncio
可以实现单线程并发IO操作。
@asyncio.coroutine
把一个generator标记为coroutine类型:
# coding: utf-8
import asyncio
@asyncio.coroutine
def helloWorld(n):
print('Hello world! %s' % n)
r = yield from asyncio.sleep(3)
print('Hello %s %s ' % (r, n))
loop = asyncio.get_event_loop()
tasks = [helloWorld(1), helloWorld(2), helloWorld(3), helloWorld(4)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
输出:
Hello world! 2
Hello world! 3
Hello world! 1
Hello world! 4
#(等待3秒左右)
Hello None 2
Hello None 1
Hello None 3
Hello None 4
程序先运行Hello world!
,然后由于asyncio.sleep()
也是一个coroutine,线程不会等待asyncio.sleep()
,而是直接中断并执行下一个消息循环。当asyncio.sleep()
返回时,线程就可以从yield from
拿到返回值(此处是None
),然后接着执行下一行语句。
asyncio
的编程模型就是一个消息循环。我们从asyncio
模块中直接获取一个EventLoop
的引用,然后把需要执行的协程扔到EventLoop
中执行,就实现了异步IO。
async/await
用asyncio提供的@asyncio.coroutine
可以把一个generator
标记为coroutine
类型,然后在coroutine
内部用yield from
调用另一个coroutine
实现异步操作。
为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async
和await
,可以让coroutine
的代码更简洁易读。
请注意,async
和await
是针对coroutine
的新语法,要使用新的语法,只需要做两步简单的替换:
- 把
@asyncio.coroutine
替换为async
; - 把
yield from
替换为await
。
上节的代码用Python3.5写:
# coding: utf-8
import asyncio
async def helloWorld(n):
print('Hello world! %s' % n)
r = await asyncio.sleep(3)
print('Hello %s %s ' % (r, n))
loop = asyncio.get_event_loop()
tasks = [helloWorld(1), helloWorld(2), helloWorld(3), helloWorld(4)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
aiohttp
aiohttp
是基于asyncio
实现的HTTP框架。
需要先安装:
$ pip install aiohttp
控制台输出:
Collecting aiohttp
Downloading aiohttp-1.3.1-cp34-cp34m-win32.whl (147kB)
100% |████████████████████████████████| 153kB 820kB/s
Collecting async-timeout>=1.1.0 (from aiohttp)
Downloading async_timeout-1.1.0-py3-none-any.whl
Collecting yarl>=0.8.1 (from aiohttp)
Downloading yarl-0.9.6-cp34-cp34m-win32.whl (77kB)
100% |████████████████████████████████| 81kB 1.8MB/s
Collecting chardet (from aiohttp)
Downloading chardet-2.3.0-py2.py3-none-any.whl (180kB)
100% |████████████████████████████████| 184kB 890kB/s
Collecting multidict>=2.1.4 (from aiohttp)
Downloading multidict-2.1.4-cp34-cp34m-win32.whl (133kB)
100% |████████████████████████████████| 143kB 3.3MB/s
Installing collected packages: async-timeout, multidict, yarl, chardet, aiohttp
Successfully installed aiohttp-1.3.1 async-timeout-1.1.0 chardet-2.3.0 multidict-2.1.4 yarl-0.9.6
说明安装完成。
示例:
# coding: utf-8
import asyncio
from aiohttp import web
@asyncio.coroutine
def index(request):
return web.Response(body=b'Hello aiohttp!', content_type='text/html')
@asyncio.coroutine
def user(request):
name = request.match_info['name']
body = 'Hello %s' % name
return web.Response(body=body.encode('utf-8'), content_type='text/html')
pass
@asyncio.coroutine
def init(loop):
# 创建app
app = web.Application(loop=loop)
#添加路由
app.router.add_route('GET', '/', index)
app.router.add_route('GET', '/user/{name}', user)
#运行server
server = yield from loop.create_server(app.make_handler(), '127.0.0.1', 9999)
print('Server is running at http://127.0.0.1:9999 ...')
loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()
运行程序:
$ python user_aiohttp.py
Server is running at http://127.0.0.1:9999 ...
浏览器依次输入查看效果:
http://127.0.0.1:9999/
http://127.0.0.1:9999/user/aiohttp
更多知识可以查看aiohttp文档:
http://aiohttp.readthedocs.io/en/stable/
参考:
1、Python asyncio库的学习和使用
http://www.cnblogs.com/rockwall/p/5750900.html
2、异步IO
http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143208573480558080fa77514407cb23834c78c6c7309000
Python学习--22 异步I/O的更多相关文章
- python学习(22) 访问数据库
原文链接:http://www.limerence2017.com/2018/01/11/python22/ 本文介绍python如何使用数据库方面的知识. SQLite SQLite是一种嵌入式数据 ...
- python学习笔记 异步asyncio
asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持. asyncio的编程模型就是一个消息循环.我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要 ...
- python学习22之函数式编程
'''''''''1.高阶函数:将函数作为参数传递到另一个函数中,作为这个函数的参数,这样的方式叫高阶函数(1)map:两个参数,一个是函数,一个是iterator,将函数依次作用于Iterator中 ...
- iOS 学习 - 22 异步解析 JSON,使用 FMDB 存储,TableView 显示
前提是已经知道了有哪些 key 值 Model 类: .h @interface ListModel : NSObject @property (nonatomic, copy)NSString *t ...
- Python学习(22):模块
转自 http://www.cnblogs.com/BeginMan/p/3183656.html 一.模块基础 1.模块 自我包含,且有组织的代码片段就是模块 模块是Pyhon最高级别的程序组织单元 ...
- Python学习-22.Python中的函数——type
type函数可以检测任何值或变量的类型. 例子: def printType(var): print(type(var)) class TestClass: pass printType(1) pri ...
- python学习-22 字符串格式化
格式化包括:百分号方式和format方式 1.百分号 - %s (%.4s 表示截取了4个字符) 传单个值: 例如: print('i am %s sex boy is ljj'%123) 运 ...
- Python学习---Python的异步IO[all]
1.1.1. 前期环境准备和基础知识 安装: pip3 install aiohttp pip3 install grequests pip3 install wheel pip3 install s ...
- Python 学习教程汇总
Python快速教程http://www.cnblogs.com/vamei/archive/2012/09/13/2682778.html简明Python教程https://bop.molun.ne ...
随机推荐
- (中等) HDU 1542 Atlantis,扫描线。
Problem Description There are several ancient Greek texts that contain descriptions of the fabled is ...
- (中等) UESTC 360 Another LCIS ,线段树+区间更新。
Description: For a sequence S1,S2,⋯,SN, and a pair of integers (i,j), if 1≤i≤j≤N and Si<Si+1<S ...
- webstorm安装express报错
.... Exit code: -1 解决方法: webstorm创建express 需要 预先安装express-generator npm install express-generator -g
- IOS开发中如何使用通知NSNotification传值
通知 是在跳转控制器之间常用的传值代理方式,除了代理模式,通知更方便.便捷,一个简单的Demo实现通知的跳转传值. 输入所要发送的信息 ,同时将label的值通过button方法调用传递, - (IB ...
- 在ubuntu中为navicat创建快捷方式
在ubuntu中,解压navicat并不会生成快捷方式,每次运行都需要进入软件解压的目录,然后运行命令开启navicat,十分不便.今天尝试引入快捷方式,直接双击运行,感觉挺不错. 首先下载一个合适的 ...
- Centos下wget下载整个网站,或者目录全部文件
需要下载某个目录下面的所有文件.命令如下 wget -c -r -np -k -L -p www.xxx.org/pub/path/ 在下载时.有用到外部域名的图片或连接.如果需要同时下载就要用-H参 ...
- Spark中的键值对操作-scala
1.PairRDD介绍 Spark为包含键值对类型的RDD提供了一些专有的操作.这些RDD被称为PairRDD.PairRDD提供了并行操作各个键或跨节点重新进行数据分组的操作接口.例如,Pa ...
- redis 高级配置
一.安全性 设置密码:在配置文件中设置 requirepass 123456 由于redis的速度非常快,每秒可以进行15万次的暴力破解,所以密码设置要强壮些 在客户端登录或者连接的时候,使用 aut ...
- BZOJ2733 永无乡【splay启发式合并】
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- --@angularJS--一个简单的UI-Router路由demo
1.index.html: <!DOCTYPE HTML><html ng-app="routerApp"><head> <titl ...