php 用yield实现异步web server

来源:互联网 时间:1970-01-01

php在php5.5的时候引入了generator和coroutine,从核心上提出了一种方法去写不阻塞的IO,当然这和node的event loop还是有比较大的区别的,它的主要理念是: 把几个大任务分别分成多个小步轮流执行,有某个小任务在等待系统io的话,就跳过它,执行下一个小任务,这样总体提升了代码的效率。

0x1:yield表达式是什么?

非常简单,描述yield表达式的只有两个关键词: 中断点和 占位符(自己总结的两点,只属于一种感性的记忆方式,并不是官方给出的专业词汇)。

举个简单的例子:

function gen() {$tid = (yield 1 + 1); for ($i = 1; $i <= 10; ++$i) {echo "This is $tid task iteration $i./n";yield $i;}}//$t1是[generator](http://php.net/manual/en/class.generator.php)类的实例(instance)$t1 = gen();//取出yield后面的表达式的结构,并没有进行赋值就暂停了当前的操作 `$tid = (yield 1 + 1) `,(特性一:中断点)$r1 = $t1->current(); //结果为 2 echo $r1; //将字符'+++'(特性二:占位符),替换到刚才暂停的地方 `$tid = '+++++'`,并进入for循环,遇见yield表达式,获取yield 表达式后面的值,并保存当前的局部变量的值,yield后面是$i,返回$ivar_dump($t1->send('+++'));

这里 current方法是暂停并返回获取当前 yield表达式的值。 send方法是先替换之前暂停时的 yield表达式所处的位置的值,再开始执行,直到遇到下一个 yield表达式,再取表达式的结果,暂停并保存当前的局部变量的值。 在这里我注意到send方法总是同时得确定两个yield表达式的位置,第一个yield表达式的值被替换,再去寻找第二个表达式的值(yield $i里面的$i),再次保存当前的状态,返回 yield表达式 后面的值。依次类推。 这里有个需要思考的问题就是如果一开始就用send方法不用current()会怎么样? 答案是send方法在第一次运行之前会隐含调用rewind方法,会在函数第一个yield的地方中断保存局部变量,但是忽略它的返回值。

0x2: coroutine是什么?

Coroutines are computer program components that generalize subroutines for nonpreemptive multitasking, by allowing multiple entry points for suspending and resuming execution at certain locations.

简单的说:coroutine(协同程序) 提供一种方法中断当前执行,保存当前的局部变量,下次再过来又可以恢复当前局部变量继续执行。在php里就是几个大的任务分别分成小的任务,轮流执行。而中断和恢复就是靠的 yield表达式来实现。

0x3: 使用 yield表达式实现非阻塞IO的例子

在这里主要有三个参与对象共同去实现任务调度: Task, Scheduler, SystemCall.

Task对象以Generator对象为参数初始化,一个 Task分成了多个小步执行。 Scheduler对象负责调度任务,什么叫做调度呢?就是分别轮流执行多个 Task对象的每一步,如果某一步还在等待IO就跳过去这一步。

SystemCall是 Task的一个小步,假设Task A 对象的多个小步为 ‘——+——‘, 执行到 +这一步就执行SystemCall的任务。

还有一些额外的对象去给任务调度添加功能:

CoroutineReturnValue把数值类型封装成类,用在处理coroutine之间的嵌套。 CoSocket封装了socket的系列操作。

Log输出日志到cli。

具体怎么实现,还是源码来的实在,仓库地址 https://github.com/Jamlee/coroutine

参考文档:

http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html



相关阅读:
Top