协程,又称微线程或纤程,是一个比进程和线程更加轻量级的解决方案,现代编程语言基本上都支持。那么协程究竟有什么特点,它是如何运作的呢?

进程和线程之间的关系我在这篇文章中做了一下比较分析。线程是程序的运行流,所有指令从头到尾按顺序一一执行,进程则是包含线程的容器。在单核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提供了比较完善的协程支持, 能帮助我们自动切换协程,可以获得极高的并发性能。