Python3 协程相关 - 学习笔记
什么是协程
协程(Coroutine),又称微线程,纤程。通常我们认为线程是轻量级的进程,因此我们也把协程理解为轻量级的线程即微线程。
协程的作用是在执行函数A时可以随时中断去执行函数B,然后中断函数B继续执行函数A(可以自由切换)。这里的中断,不是函数的调用,而是有点类似CPU的中断。这一整个过程看似像多线程,然而协程只有一个线程执行。
协程的优势
- 执行效率极高,因为是子程序(函数)切换不是线程切换,由程序自身控制,没有切换线程的开销。所以与多线程相比,线程的数量越多,协程的性能优势越明显。
- 不需要锁机制,因为只有一个线程,也不存在同时写变量冲突,在控制共享资源时也不需要加锁,只需要判断状态,因此执行效率高的多。
- 协程可以处理IO密集型程序的效率问题,但不适合处理CPU密集型程序,如要充分发挥CPU利用率应结合多进程+协程。
Python3中的协程
生成器 yield/send
yield + send(利用生成器实现协程)
例:
def simple_coroutine():
print('-> start')
x = yield
print(x)
print('-> end')
#主线程
sc = simple_coroutine()
# 可以使用sc.send(None),效果一样
next(sc) #预激
sc.send('go')
执行结果如下:
-> start
go
-> end
抛出StopIteration
simple_coroutine()是一个生成器,由next(sc)预激,启动协程,执行到第一个yield中断send()方法给yield传入参数,继续执行
协程的四个状态
- GEN_CREATED:等待开始执行
- GEN_RUNNING:解释器正在执行
- GEN_SUSPENED:在yield表达式处暂停
- GEN_CLOSED:执行结束
协程终止
- 协程中未处理的异常会向上冒泡,传给 next 函数或 send 方法的调用方(即触发协程的对象)
- 终止协程的一种方式:发送某个哨符值,让协程退出。内置的 None 和Ellipsis 等常量经常用作哨符值
@asyncio.coroutine和yield from
asyncio.coroutione
asyncio是Python3.4版本引入的标准库,直接内置了对异步IO的支持。asyncio的异步操作,需要在coroutine中通过yield from完成。
例:
import asyncio
@asyncio.coroutine
def test(i):
print('test_1', i)
r = yield from asyncio.sleep(1)
print('test_2', i)
loop = asyncio.get_event_loop()
tasks = [test(i) for i in range(1,3)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
@asyncio.coroutine把一个generator标记为coroutine类型,然后就把这个coroutine扔到EventLoop中执行。test()会首先打印出test_1- 然后
yield from语法可以让我们方便地调用另一个generator。由于asyncio.sleep()也是一个coroutine,所以线程不会等待asyncio.sleep(),而是直接中断并执行下一个消息循环。 - 当
asyncio.sleep()返回时,线程就可以从yield from拿到返回值(此处是None) - 然后接着执行下一行语句。把
asyncio.sleep(1)看成是一个耗时1秒的IO操作,在此期间主线程并未等待,而是去执行EventLoop中其他可以执行的coroutine了,因此可以实现并发执行
结果如下:
test_1 1
test_1 2
test_2 1
test_2 2
yield from
当yield from 后面加上一个生成器后,就实现了生成的嵌套。
实现生成器的嵌套,并不是一定必须要使用yield from,而是使用yield from可以让我们避免让我们自己处理各种料想不到的异常,而让我们专注于业务代码的实现。
如果自己用yield去实现,那只会加大代码的编写难度,降低开发效率,降低代码的可读性。
- 调用方:调用委派生成器的客户端(调用方)代码
- 委托生成器:包含yield from表达式的生成器函数
- 子生成器:yield from后面加的生成器函数
# 子生成器
def average_gen():
total = 0
count = 0
average = 0
while True:
new_num = yield average
count += 1
total += new_num
average = total/count
# 委托生成器
def proxy_gen():
while True:
yield from average_gen()
# 调用方
def main():
calc_average = proxy_gen()
next(calc_average)# 预激下生成器
print(calc_average.send(10))
print(calc_average.send(20))
print(calc_average.send(30))
main()
结果如下:
10.0
15.0
20.0
委托生成器的作用:
在调用方与子生成器之间建立一个双向通道。
双向通道就是调用方可以通过send()直接发送消息给子生成器,而子生成器yield的值,也是直接返回给调用方。
为什么要用yield from
yield from帮我们做了很多的异常处理,而这些如果我们要自己去实现的话,一个是编写代码难度增加,写出来的代码可读性很差,很可能有遗漏,只要哪个异常没考虑到,都有可能导致程序崩溃。
async/await关键字
为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读。
可将上面的案例代码改造如下:
import asyncio
# @asyncio.coroutine
async def test(i):
print('test_1', i)
# r = yield from asyncio.sleep(1)
await asyncio.sleep(1)
print('test_2', i)
loop = asyncio.get_event_loop()
tasks = [test(i) for i in range(1,3)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
执行结果
test_1 1
test_1 2
test_2 1
test_2 2
async替换@asyncio.coroutine,加在def之前用于修饰await替换yield from
Python3 协程相关 - 学习笔记的更多相关文章
- python中和生成器协程相关的yield from之最详最强解释,一看就懂(四)
如果认真读过上文的朋友,应该已经明白了yield from实现的底层generator到caller的上传数据通道是什么了.本文重点讲yield from所实现的caller到coroutine的向下 ...
- python中和生成器协程相关的yield之最详最强解释,一看就懂(一)
yield是python中一个非常重要的关键词,所有迭代器都是yield实现的,学习python,如果不把这个yield的意思和用法彻底搞清楚,学习python的生成器,协程和异步io的时候,就会彻底 ...
- 数论算法 剩余系相关 学习笔记 (基础回顾,(ex)CRT,(ex)lucas,(ex)BSGS,原根与指标入门,高次剩余,Miller_Rabin+Pollard_Rho)
注:转载本文须标明出处. 原文链接https://www.cnblogs.com/zhouzhendong/p/Number-theory.html 数论算法 剩余系相关 学习笔记 (基础回顾,(ex ...
- python中和生成器协程相关yield from之最详最强解释,一看就懂(二)
一. 从列表中yield 语法形式:yield from <可迭代的对象实例> python中的列表是可迭代的, 如果想构造一个生成器逐一产生list中元素,按之前的yield语法,是在 ...
- ReentrantLock 相关学习笔记
ReentrantLock 相关学习笔记 标签(空格分隔): Java多线程 Java接口Lock有三个实现类:ReentrantLock.ReentrantReadWriteLock.ReadLoc ...
- 关于协程的学习 & 线程栈默认10M
先看的这篇文章:http://blog.csdn.net/qq910894904/article/details/41699541 以nginx为代表的事件驱动的异步server正在横扫天下,那么事件 ...
- Python学习---线程/协程/进程学习 1220【all】
Python学习---线程基础学习 Python学习---线程锁/信号量/条件变量同步1221 Python学习---同步条件event/队列queue1223 Python学习---进程 1225 ...
- 【python之路36】进程、线程、协程相关
线程详细用法请参考:http://www.cnblogs.com/sunshuhai/articles/6618894.html 一.初始多线程 通过下面两个例子的运行效率,可以得知多线程的速度比单线 ...
- python 协程的学习记录
协程是个子程序,执行过程中,内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行 从句法上看,协程与生成器类似,都是定义体中包含 yield 关键字的函数.可是,在协程中,yield 通常 ...
随机推荐
- ubuntu18.04编译jdk8
准备编译环境 sudo apt-get install -y zip unzip build-essential libx11-dev libxext-dev libxrender-dev libxt ...
- Leetcode 题目整理
1. Two Sum Given an array of integers, return indices of the two numbers such that they add up to a ...
- 十二、sed文本处理
一.概述 1.sed 是一款流编辑工具,用来对文本进行过滤与替换工作,特别是当你想要对几十个配置文件做统计修改时,你会感受到 sed 的魅力!sed 通过输入读取文件内容,但一次仅读取一行内容进行某些 ...
- 06讲案例篇:系统的CPU使用率很高,但为啥却找不到高CPU的应用
小结 碰到常规问题无法解释的 CPU 使用率情况时,首先要想到有可能是短时应用导致的问题,比如有可能是下面这两种情况. 第一,应用里直接调用了其他二进制程序,这些程序通常运行时间比较短,通过 top ...
- 手把手实操教程!使用k3s运行轻量级VM
前 言 k3s作为轻量级的Kubernetes发行版,运行容器是基本功能.VM的管理原本是IaaS平台的基本能力,随着Kubernetes的不断发展,VM也可以纳入其管理体系.结合Container和 ...
- for实例
#-*- coding:utf-8 *-* salary = 5000 shop_list = [('iphone',9000),('mac book',10000),('python book',9 ...
- JavaScript 与 Java 有什么不同?
JavaScript 编程语言是由 Netscape,Inc. 开发的,它并不是 Java 平台的一部分. JavaScript 不会创建小应用程序或独立应用程序.在最常见的形式中,JavaScrip ...
- 通过Python包来剪枝、蒸馏DNN
用 Distiller 压缩 PyTorch 模型 作者: PyTorch 中文网发布: 2018年7月15日 5,101阅读 0评论 近日,Intel 开源了一个用于神经网络压缩的开源 Python ...
- Scala 学习(9)之「函数式编程」
引用透明 对相同的输入,总是能得到相同的输出. 如果 f(x) 的参数 x 和函数体都是引用透明的,那么函数 f 是纯函数. 违反引用透明的例子 我们可以很清楚的看到,对于相同的输入,第二次调用app ...
- Unity酱~ 卡通渲染技术分析(一)
前面的话 unitychan是日本unity官方团队提供的一个Demo,里面有很好的卡通渲染效果,值得参考学习 上图是我整理出来的shader结构,可以看到Unity娘被拆分成了很多个小的部件,我想主 ...