yield个人认为其实是为了实现协程而出现的。所以如果要解释清楚什么是yield,那么也就必须要先搞懂什么是协程。首先明确一点:协程是针对单个CPU的,也就是说,讲协程讲的就是单线程。我们可以通过协程实现类似并发的任务,并且如果只是在一个CPU上的话,使用协程带来的效率一般都会比使用线程来的高。这是为啥呢?这就要看协程的原理了。

协程的原理很简单,打个比方就能讲明白了:假设说有十个人去食堂打饭,这个食堂比较穷,只有一个打饭的窗口,并且也只有一个打饭阿姨,那么打饭就只能一个一个排队来打咯。这十个人胃口很大,每个人都要点5个菜,但这十个人又有个毛病就是做事情都犹豫不决,所以点菜的时候就会站在那里,每点一个菜后都会想下一个菜点啥,因此后面的人等的很着急呀。这样一直站着也不是个事情吧,所以打菜的阿姨看到某个人犹豫5秒后就开始吼一声,会让他排到队伍最后去,先让别人打菜,等轮到他的时候他也差不多想好吃啥了。这确实是个不错的方法,但也有一个缺点,那就是打菜的阿姨会的等每个人5秒钟,如果那个人在5秒内没有做出决定吃啥,其实这5秒就是浪费了。一个人点一个菜就是浪费5秒,十个人每个人点5个菜可就浪费的多啦(菜都凉了要)。那咋办呢?这个时候阿姨发话了:大家都是学生,学生就要自觉,我以后也不主动让你们排到最后去了,如果你们觉得自己会犹豫不决,就自己主动点直接点一个菜就站后面去,等下次排到的时候也差不多想好吃啥了。这个方法果然有效,大家点了菜后想的第一件事情不是下一个菜吃啥,而是自己会不会犹豫,如果会犹豫那直接排到队伍后面去,如果不会的话就直接接着点菜就行了。这样一来整个队伍没有任何时间是浪费的,效率自然就高了。

这个例子里的排队阿姨的那声吼就是我们的CPU中断,用于切换上下文。每个打饭的学生就是一个task。而每个人自己决定自己要不要让出窗口的这种行为,其实就是我们协程的核心思想。

在用线程的时候,其实虽然CPU把时间给了你,你也不一定有活干,比如你要等IO、等信号啥的,这些时间CPU给了你你也没用呀。

在用协程的时候,CPU就不来分配时间了,时间由你们自己决定,你觉得干这件事情很耗时,要等IO啥的,你就干一会歇一会,等到等IO的时候就主动让出CPU,让别人上去干活,别人也是讲道理的,干一会也会把时间让给你。协程就是使用了这种思想,让编程者控制各个任务的运行顺序,从而最大可能的发挥CPU的性能。

协程的实现在网络上有一些代码,主要是通过把当前的寄存器暂存起来,然后以后回来的时候再恢复这些寄存器来实现的。我们可以把它想象成即便是单个线程,也有多个栈来存放线程上下文切换是的寄存器。而且这种切换是线程主动进行的,不是由时钟中断产生的。

协程的关键在于可以在函数中跳出来,把CPU让给别人,然后当别人用完CPU后,可以从函数跳出来的地方去继续执行。

Python在2.X里还不能很好的支持协程,调回去的那一步一般需要用循环来通过迭代器来做。

比如:
def A():
print ‘a1’
yield B()
print ‘a2’

def B():
print ‘b1’
yield A()
print ‘b2’

如果是正规的协程,那么运行的结果应该是:
a1
b1 #注意,从这一步开始往后是从A()的yield后面开始执行,没有协程的话做不到这个功能
a2
b2

但目前的Python还做不到这么牛,B()中调用yield A()不是从中断的地方继续,而是从头开始,就和普通的函数一样,因此如果要达到效果的话只能:

def B():
yield ‘b1’
yield ‘b2’

def A():
b = B() #得到一个迭代器
print ‘a1’
print b.next()
print ‘a2’
print b.next()

有时候我们会在函数里把yield的输出赋值给一个变量,直接赋值的话会的给出None,需要用send的方法,如:
import time

def dosth():
time.sleep(1)
return 9

def bar():
r = yield dosth()
print r

f = bar()
r = f.next()
print r
f.send(r) #这里通过send,把r传递给yeild赋值的对象

那么协程有什么用呢?最简单的,协程可以在不需要的时候把CPU让出,比如对于一些异步的方法:
result01 = yield ASYN_METHOD01()
result02 = yield ASYN_METHOD02(result01)
当调用这个后,由于ASYN_METHOD01()是异步方法,因此这个立马就返回了,返回后由manager继续执行,manager继续干很多别的事情,然后可以通过某种途径知道这个方法的结果,然后再send(result01),然后继续。这即能节省时间CPU,还能把多个异步调用方法顺序写出来!很牛叉。