协程与yield

协程,又称微线程或纤程,是一个比进程和线程更加轻量级的解决方案,现代编程语言基本上都支持。那么协程究竟有什么特点,它是如何运作的呢?
协程与yield
进程和线程之间的关系我在这篇文章中做了一下比较分析。线程是程序的运行流,所有指令从头到尾按顺序一一执行,进程则是包含线程的容器。在单核CPU中,永远只有一条线程在运行,当然也只可能有一个进程运行,由操作系统内核负责进程和线程的切换。而协程是属于用户空间的,操作系统对其一无所知,由用户自己去做调度。显然用户自己调度比通过使用一个中断让操作系统调度会快很多,这就是协程快的秘诀了。

那么,协程究竟是一种什么样的存在呢?上面说到线程是一条指令的执行流,那么这个流的则由函数这些最小执行单元组成,函数则可能是包涵或者调用关系,无论是那种,一个函数只有只能完毕,下一个函数才能被执行,例如有两个函数A和B,A由于某些原因被阻塞,那么B肯定就不能运行。协程的出现就是为了解决这个问题,函数可以随时中断,去调用另外的函数。

Python和PHP5.5以上版本(包含)中,可以利用生成器中的yield在一定程度上实现协程。例如在一个车流量多的路口,摄像头获取车牌号,并保存到文本文件可以这样做:

#!/usr/bin/env python
import random

def getNum(action):
    action.next()
    while True:
        num=random.randint(1000, 9999) #获取车牌号,这里随机
        stored = action.send(num)
        if stored > 500:
            break
    action.close()


def storage():
    r=0
    while True:
        f=open("./car.txt","a")
        num = yield r
        print num
        if num:
            f.write(str(num)+"\n")
            r=r+1
    f.close()


if __name__ == "__main__":
    S=storage()
    getNum(S)
<?php
function getNum($action){
    while(1){
        $num=rand(1000,9999);
        $stored = $action->send($num);
        if($stored > 500){
            break;
        }
    }
}

function storage(){
    $r=0;
    while(1){
        $num=(yield $r);
        if($num){
            echo $num."\n";
            file_put_contents("./car.txt",$num."\n",FILE_APPEND);
            $r++;
        }
    }
}

$S=storage();
getNum($S);

yield这个关键字在很多语言中都有应用,在PHP和Python中,如果一个函数中含有yield,则这个函数会成为一个生成器(Generator)。上面的例子中,函数之间相互调用并没有在一个函数结束之后,另一个函数才开始运行,而是相互协作,所以称为”协程”,而非线程的抢占式多任务。需要注意的是Python的yield需要用next来启动,而PHP不需要。

yield只能在一定程度的实现协程,要更加深入理解和使用协程,可以尝试使用gevent,gevent为Python提供了比较完善的协程支持, 能帮助我们自动切换协程,可以获得极高的并发性能。

发表回复