来源:Python与数据分析 链接:

https://mp.weixin.qq.com/s/GrU6C-x4K0WBNPYNJBCrMw

什么是协程

引用官方的说法:

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

与线程相比,协程更轻量。一个Python线程大概占用8M内存,而一个协程只占用1KB不到内存。协程更适用于IO密集型的应用。

在讲述协程的实现前,我们有必要先来看一下send方法。

send方法

yield表达式有一个返回值,send方法的作用就是控制这个返回值,send的参数就是yield表达式的返回值。我们来看一下官方文档上关于send的定义

generate.send(value):生成器的send(value)方法会将value值“发送”给生成器中的方法。value参数变成当前yield表达式的值。send()方法会返回生成器生成的下一个yield值或者StopIteration异常(如果生成器没有生成下一个yield值就退出了)。当通过调用send()启动生成器时,value值必须为None,因为当前还没有yield表达式可以接收参数。

是不是看晕了?我们来看一个例子

def func():
while True:
print("before yield")
x = yield
print("after yield:",x) g = func()
next(g) # 程序运行到yield并停在该处,等待下一个next
g.send(1) # 给yield发送值1,这个值被赋值给了x,并且打印出来,然后继续下一次循环停在yield处
g.send(2) # 给yield发送值2,这个值被赋值给了x,并且打印出来,然后继续下一次循环停在yield处
next(g) # 没有给x赋值,执行print语句,打印出None,继续循环停在yield处

上面的代码输出

before yield
after yield: 1
before yield
after yield: 2
before yield
after yield: None
before yield

第一次调用next的时候,程序从函数最开始处运行,打印出

before yield

执行到yield处,停在该处。

接下来,向生成器send(1)。send在这里起到两个作用,一个是将参数值赋给yield的返回值,然后该返回值赋给了变量x;一个是继续程序的执行,直到下一次遇到yield停下来。第二个功能和next类似。其实,next 就相当于 send(None) 。

执行了 send(1) 后,x被赋值给yield的返回值,即send的参数1,并继续往下执行,打印出了

after yield: 1

继续执行,回到循环的开始,向下执行,打印出

before yield

再次遇到yield,停在该处,等待下一次send或next的调用。

向生成器send(2)。这里的步骤和 send(1) 相同,打印出下面两条后,在yield处停住。

after yield: 2

before yield

执行 next(g),x被赋值为yield表达式的返回值,也就是None,继续向下执行,打印出

after yield: None

再次回到循环的开始,向下执行,打印出

before yield

程序运行结束。

现在是不是有点理解send了?

yield和send实现Python协程

我们来用协程实现一个生产者/消费者的例子

import time
def consume():
r = ''
while True:
n = yield r
if not n:
return
print('[consumer] consuming %s...' % n)
time.sleep(1)
r = 'well received' def produce(c):
next(c)
n = 0
while n < 5:
n = n + 1
print('[producer] producing %s...' % n)
r = c.send(n)
print('[producer] consumer return: %s' % r)
c.close() if __name__=='__main__': c = consume() produce(c)

运行上面的程序,会输出

[producer] producing 1...
[consumer] consuming 1...
[producer] consumer return: well received
[producer] producing 2...
[consumer] consuming 2...
[producer] consumer return: well received
[producer] producing 3...
[consumer] consuming 3...
[producer] consumer return: well received
[producer] producing 4...
[consumer] consuming 4...
[producer] consumer return: well received
[producer] producing 5...
[consumer] consuming 5...
[producer] consumer return: well received

produce函数负责生产数据,consume函数负责消费数据。具体执行过程如下:

  1. 首先调用consume函数,consume函数的返回是一个生成器,把这个生成器传入produce函数。
  2. produce函数中调用next(c)启动生成器。
  3. 计算 n = n+1 生成数据,一旦生产了数据,调用 c.send(n) 切换到consume执行。
  4. consume函数中拿到数据后赋值给n,继续执行yield后面的语句。
  5. consume函数中打印消费的数据,并设置返回值r,又回到循环的开始,通过yield把结果传回。
  6. produce拿到consume返回的值,继续生产下一个数据。
  7. 5个数据生产完毕后,循环结束,通过c.close()关闭consume,结束全过程。

produce和consume函数在一个线程内执行,通过调用send方法和yield互相切换,实现协程的功能。

00.用 yield 实现 Python 协程的更多相关文章

  1. 用yield实现python协程

    刚刚介绍了pythonyield关键字,趁热打铁,现在来了解一下yield实现协程. 引用官方的说法: 与线程相比,协程更轻量.一个python线程大概占用8M内存,而一个协程只占用1KB不到内存.协 ...

  2. Python协程笔记 - yield

    生成器(yield)作为协程 yield实际上是生成器,在python 2.5中,为生成器增加了.send(value)方法.这样调用者可以使用send方法对生成器发送数据,发送的数据在生成器中会赋值 ...

  3. 终结python协程----从yield到actor模型的实现

    把应用程序的代码分为多个代码块,正常情况代码自上而下顺序执行.如果代码块A运行过程中,能够切换执行代码块B,又能够从代码块B再切换回去继续执行代码块A,这就实现了协程 我们知道线程的调度(线程上下文切 ...

  4. 从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 ...

  5. 从yield到yield from再到python协程

    yield 关键字 def fib(): a,b = 0,1 while 1: yield b a,b = b,a+b yield是在:PEP 255 -- Simple Generators 这个p ...

  6. Python 协程总结

    Python 协程总结 理解 协程,又称为微线程,看上去像是子程序,但是它和子程序又不太一样,它在执行的过程中,可以在中断当前的子程序后去执行别的子程序,再返回来执行之前的子程序,但是它的相关信息还是 ...

  7. day-5 python协程与I/O编程深入浅出

    基于python编程语言环境,重新学习了一遍操作系统IO编程基本知识,同时也学习了什么是协程,通过实际编程,了解进程+协程的优势. 一.python协程编程实现 1.  什么是协程(以下内容来自维基百 ...

  8. [转载] Python协程从零开始到放弃

    Python协程从零开始到放弃 Web安全 作者:美丽联合安全MLSRC   2017-10-09  3,973   Author: lightless@Meili-inc Date: 2017100 ...

  9. python协程详解

    目录 python协程详解 一.什么是协程 二.了解协程的过程 1.yield工作原理 2.预激协程的装饰器 3.终止协程和异常处理 4.让协程返回值 5.yield from的使用 6.yield ...

随机推荐

  1. error: &#39;for&#39; loop initial declarations are only allowed in C99 mode

    error: 'for' loop initial declarations are only allowed in C99 mode 使用gcc编译代码是报出 error: 'for' loop i ...

  2. 2015南阳CCPC C - The Battle of Chibi DP树状数组优化

    C - The Battle of Chibi Description Cao Cao made up a big army and was going to invade the whole Sou ...

  3. luogu3959 宝藏 搜索+剪枝

    题目大意 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中的宝藏.但是 ...

  4. PCB 使用第3方网站做为外链图片资源

    计划将Web版SI9000阻抗计算工具放外网了,由于个人网站带宽太小原因, 准备将静态的图片资源放在第三方网站,今天找了好几个图床网站,把阻抗模型图上传到图床网站,这样一来就能解决带宽的问题了,今天折 ...

  5. codevs1026商务旅行

    1036 商务旅行  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题解       题目描述 Description 某首都城市的商人要经常到各城镇去做 ...

  6. JavaScript--引用JS外部文件

    通过前面知识学习,我们知道使用<script>标签在HTML文件中添加JavaScript代码,如图: JavaScript代码只能写在HTML文件中吗?当然不是,我们可以把HTML文件和 ...

  7. 363 Max Sum of Rectangle No Larger Than K 最大矩阵和不超过K

    Given a non-empty 2D matrix matrix and an integer k, find the max sum of a rectangle in the matrix s ...

  8. 软件图标显示不正常【win7企业版】

    现象: 原因: 图标缓存没有把该软件图标建立起来 解决: 一. 1.找到 IconCache.db 2.你要把电脑隐藏文件打开不然找不到这个文件的,组织—文件夹及搜索选项——查看——显示隐藏文件.文件 ...

  9. String字符串的完美度

    题目详情: 我们要给每个字母配一个1-26之间的整数,具体怎么分配由你决定,但不同字母的完美度不同, 而一个字符串的完美度等于它里面所有字母的完美度之和,且不在乎字母大小写,也就是说字母F和f的完美度 ...

  10. win32动态库

    先讲一个基本的动态库,功能为自定义一个动态库,里面有一个函数MyMessage实现弹出MessageBox. 1. 先在头文件中定义: #ifdef __cplusplus #define EXPOR ...