Python协程(上)
几个概念:
- event_loop 事件循环:程序开启一个无限的循环,程序员会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。
- coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
- task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。
- future: 代表将来执行或没有执行的任务的结果。它和task上没有本质的区别
- async/await 关键字:python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。
定义协程
通过async关键字定义一个协程(coroutine),协程也是一种对象。协程不能直接运行,需要把协程加入到事件循环(loop),由后者在适当的时候调用协程。asyncio.get_event_loop方法可以创建一个事件循环,然后使用run_until_complete将协程注册到事件循环,并启动事件循环。
import time
import asyncio
async def task(x):
print('Waiting: ', x)
await asyncio.sleep(x)
start = time.time()
coroutine = task(2)
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine)
print('用时:', time.time()-start)
创建task
协程对象不能直接运行,在注册事件循环的时候,其实是run_until_complete方法将协程包装成为了一个任务(task)对象。所谓task对象是Future类的子类。保存了协程运行后的状态,用于未来获取协程的结果。
import asyncio
import time
async def task(x):
print('Waiting: ', x)
await asyncio.sleep(x)
start = time.time()
coroutine = task(2)
loop = asyncio.get_event_loop()
task = loop.create_task(coroutine) # task = asyncio.ensure_future(coroutine)
print(task)
loop.run_until_complete(task)
print(task)
print('用时:', time.time() - start)
创建task后,task在加入事件循环之前是pending状态,执行之后是finished状态。
asyncio.ensure_future(coroutine) 和loop.create_task(coroutine)
都可以创建一个task,run_until_complete的参数是一个futrue对象。当传入一个协程,其内部会自动封装成task,task是Future的子类。isinstance(task, asyncio.Future)将会输出True。
绑定回调
绑定回调,在task执行完毕的时候可以获取执行的结果,回调的最后一个参数是future对象,通过该对象可以获取协程返回值。如果回调需要多个参数,可以通过偏函数导入。
import time
import asyncio
async def task(x):
print('Waiting: ', x)
await asyncio.sleep(x)
return 'Done after {}s'.format(x)
def callback(future):
print('Callback: ', future.result())
coroutine = task(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
task.add_done_callback(callback)
loop.run_until_complete(task)
coroutine执行结束时候会调用回调函数。并通过参数future获取协程执行的结果。我们创建的task和回调里的future对象,实际上是同一个对象。
future 与 result
回调中我们使用了future对象的result方法。前面不绑定回调的例子中,我们可以看到task有fiinished状态。在那个时候,可以直接读取task的result方法。
async def task(x):
print('Waiting {}'.format(x))
return 'Done after {}s'.format(x)
coroutine = task(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)
# task.result()是协程对象的返回值
print('Task result: {}'.format(task.result()))
阻塞和await
使用async可以定义协程对象,使用await可以针对耗时的操作进行挂起,就像生成器里的yield一样,函数让出控制权。协程遇到await,事件循环将会挂起该协程,执行别的协程,直到其他的协程也挂起或者执行完毕,再进行下一个协程的执行。
耗时的操作一般是一些IO操作,例如网络请求,文件读取等。我们使用asyncio.sleep函数来模拟IO操作。协程的目的也是让这些IO操作异步化。
import asyncio
import time
async def task(x):
print('Waiting: ', x)
await asyncio.sleep(x)
return 'Done after {}s'.format(x)
start = time.time()
coroutine = task(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)
print('Task result: ', task.result())
print('Time: ', time.time() - start)
在 sleep的时候,使用await让出控制权。即当遇到阻塞调用的函数的时候,使用await方法将协程的控制权让出,以便loop调用其他的协程。现在我们的例子就用耗时的阻塞操作了。
并发和并行
asyncio实现并发,就需要多个协程来完成任务,每当有任务阻塞的时候就await,然后其他协程继续工作。创建多个协程的列表,然后将这些协程注册到事件循环中。
import asyncio
import time
async def task(x):
print('Waiting: ', x)
await asyncio.sleep(x)
return 'Done after {}s'.format(x)
start = time.time()
coroutine1 = task(1) # <coroutine object task at 0x000001E583341258>
coroutine2 = task(2)
coroutine3 = task(4)
loop = asyncio.get_event_loop()
tasks = [ # 创建任务
asyncio.ensure_future(coroutine1),
asyncio.ensure_future(coroutine2),
asyncio.ensure_future(coroutine3)
]
loop.run_until_complete(asyncio.wait(tasks))
for task in tasks:
print('Task result: ', task.result())
print('Time: ', time.time() - start)
# 当任务比较多的时候,可以使用列表生成式,效果是一样的。
import asyncio
import time
async def task(x):
print('Waiting: ', x)
await asyncio.sleep(x)
return 'Done after {}s'.format(x)
start = time.time()
loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(task(i)) for i in [1,2,4]]
loop.run_until_complete(asyncio.wait(tasks))
for task in tasks:
print('Task result: ', task.result())
print('Time: ', time.time() - start)
使用aysncio实现了并发。asyncio.wait(tasks) 也可以使用 asyncio.gather(*tasks) ,前者接受一个task列表,后者接收一堆task。
Python协程(上)的更多相关文章
- Python 协程总结
Python 协程总结 理解 协程,又称为微线程,看上去像是子程序,但是它和子程序又不太一样,它在执行的过程中,可以在中断当前的子程序后去执行别的子程序,再返回来执行之前的子程序,但是它的相关信息还是 ...
- day-5 python协程与I/O编程深入浅出
基于python编程语言环境,重新学习了一遍操作系统IO编程基本知识,同时也学习了什么是协程,通过实际编程,了解进程+协程的优势. 一.python协程编程实现 1. 什么是协程(以下内容来自维基百 ...
- 终结python协程----从yield到actor模型的实现
把应用程序的代码分为多个代码块,正常情况代码自上而下顺序执行.如果代码块A运行过程中,能够切换执行代码块B,又能够从代码块B再切换回去继续执行代码块A,这就实现了协程 我们知道线程的调度(线程上下文切 ...
- 从yield 到yield from再到python协程
yield 关键字 def fib(): a, b = 0, 1 while 1: yield b a, b = b, a+b yield 是在:PEP 255 -- Simple Generator ...
- 用yield实现python协程
刚刚介绍了pythonyield关键字,趁热打铁,现在来了解一下yield实现协程. 引用官方的说法: 与线程相比,协程更轻量.一个python线程大概占用8M内存,而一个协程只占用1KB不到内存.协 ...
- [转载] Python协程从零开始到放弃
Python协程从零开始到放弃 Web安全 作者:美丽联合安全MLSRC 2017-10-09 3,973 Author: lightless@Meili-inc Date: 2017100 ...
- python 协程的学习记录
协程是个子程序,执行过程中,内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行 从句法上看,协程与生成器类似,都是定义体中包含 yield 关键字的函数.可是,在协程中,yield 通常 ...
- python协程--yield和yield from
字典为动词“to yield”给出了两个释义:产出和让步.对于 Python 生成器中的 yield 来说,这两个含义都成立.yield item 这行代码会产出一个值,提供给 next(...) 的 ...
- 00.用 yield 实现 Python 协程
来源:Python与数据分析 链接: https://mp.weixin.qq.com/s/GrU6C-x4K0WBNPYNJBCrMw 什么是协程 引用官方的说法: 协程是一种用户态的轻量级线程,协 ...
- python协程详解
目录 python协程详解 一.什么是协程 二.了解协程的过程 1.yield工作原理 2.预激协程的装饰器 3.终止协程和异常处理 4.让协程返回值 5.yield from的使用 6.yield ...
随机推荐
- Qt如何获得窗口的几何信息(Window Geometry)
一个窗口除去窗框以后,余下部分称为客户区域.获得窗口的几何信息(坐标,长宽等)有以下两组命令: 1. 包括窗框(即整个窗口):x(), y(), frameGeometry(), pos(), mo ...
- PhotoSwipe 图片浏览插件使用方法
一.简介 PhotoSwipe 是专为移动触摸设备设计的相册/画廊.兼容所有iPhone.iPad.黑莓6+,以及桌面浏览器.底层实现基于HTML/CSS/JavaScript,是一款免费开源的相册产 ...
- Spring bean - scope详解
Scope是定义Spring如何创建bean的实例的. 在创建bean的时候可以带上scope属性,scope有下面几种类型. Singleton 这也是Spring默认的scope,表示Spring ...
- 【BZOJ】2243 [SDOI2011]染色
[算法]树链剖分+线段树 [题解] 树链剖分算法:http://www.cnblogs.com/onioncyc/p/6207462.html 定义线段树结构体有l,r,lc,rc,sum,data. ...
- bugku数字验证绕过正则
题目:http://120.24.86.145:9009/21.php 第6行使用正则匹配如果匹配到$password开头12个字符中有空格则输出flag并执行exit; 12行是正则匹配$passw ...
- How to insert a segment of noise to music file
如何向音频文件中插入噪声 为了研究噪声和音乐对EEG的的影响,实验前需要准备一段夹杂噪声的音乐. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ...
- Nginx-1.6.3源码安装、虚拟主机
源码安装nginx cat /etc/redhat-release uname -rm yum install pcre-devel openssl-devel -y rpm -qa pcre pcr ...
- JavaScript变量命名规则:匈牙利命名法
匈牙利命名法语法 变量名=类型+对象描述 类型指变量的类型 对象描述指对象名字全称或名字的一部分,要求有明确含义,命名要容易记忆容易理解. 提示 虽然JavaScript变量表面上没有类型,但是Jav ...
- vmware linux虚拟机连接ip设置
首先: 点击VMware 编辑->虚拟网络编辑器: 然后选中VMnet8的查看NAT设置: 上图第二步(记下红框中网关地址和子网掩码): 第三步(用于设置虚拟机地址范围): 接下来就是设置虚拟机 ...
- xcode没有ios7的模拟器
xcode7 目前只支持 ios8盒和iOS9的模拟器如果是Yosemite系统,下载xcode7和xcode6.4,两个版本可以共存,然后再下载iOS7默契你如果是EI Caption系统,网上说E ...