php协程效率,PHP协程之路

上一篇文章,我们讲到了协程的意义,以及它出现是为了解决哪些痛点。也知道了,协程其实相当于在用户空间,实现一个操作系统的线程调度器。当然从某种意义上说,和操作系统的调度器也不太一样。不过总的来说需要实现以下功能:1.在代码的某一行挂起。2.在某个时间阶段再从刚才挂起的地方拉起,继续向下执行。说白了就是保存现场和恢复现场。在汇编层面,这就比较简单了,保存各个寄存器的值,等需要的时候恢复就可以了。剩下的就是一下内存空间的管理,这里就先不管。

总之,如果要实现切换,必须要深入到底层去操作。而PHP作为一个上层的语言,可以说操作底层比较麻烦。除非PHP虚拟机给你提供了相关功能。

在python中,我知道有一个yield语法可以实现函数在某个地方中断,在从某个地方拉起运行。结果后来居然发现PHP也有这个语法,不愧为世界上最好的语言(逃

认识yield

yield是PHP生成器的一个语法。yield和return一样,都会返回数据,但是最大的不同点在于return会结束掉整个函数,下一次再执行,会从头开始。比如<?php

function retFunc(){

return true;

echo "hello world";

}

retFunc();

retFunc();

retFunc();

这个代码,无论retFunc()这个函数调用多少次,“hello world”永远不会输出出来,因为在打印之前就已经return了,表示函数已经结束。

那如果情况是下面这样<?php

function yieldFunc(){

yield true;

echo "hello world";

yield true;

echo "hello world again";

}

yieldFunc();

yieldFunc();

yieldFunc();

再一次我们用yield来代替return。试试看,诶??好像还是没有任何值打印出来?

这是因为生成器的调用和普通的函数调用不一样。

我们用var_dump来看看他们之间的区别var_dump( yieldFunc() );

var_dump( retFunc() );

你会发现,输出如下信息object(Generator)#1 (0) {

}

bool(true)

可以看到,retFunc函数返回了一个bool型数据,这个数据是我们return的时候的数据。而yieldFunc并没有返回bool型数据(虽然我们yield true;),而是返回一个Object(Generator)这是一个生成器对象。也就是说,你每一次调用yieldFunc()都是返回一个生成器对象。

如果要执行这个生成器,要按照生成器对象自己的规则来。

使用yield

好了,那么问题来了,我们知道现在函数返回了一个生成器,接下来我们要去学会如何使用它。就像你花大价钱买了一个榴莲回家,结果不知道怎么打开榴莲,那就太浪费了。如果你在生活中发生这种事情,千万要冷静,不能惊慌。用袋子把榴莲包起来装好,马上打电话给我,我会去办你解决掉这个大烦恼(斜眼笑)。

最简单的方法:foreach

PHP的foreach能处理这个迭代器。毕竟foreach本来就是处理循环迭代用的。

假设现在我们有一个yield的函数function yieldNum(){

yield 1;

yield 2;

yield 3;

yield 4;

yield 5;

}

然后我们就可以使用foreach来调用它foreach (yieldNum() as $num) {

echo $num, "n";

}

最后会输出1

2

3

4

5

哦,对了,简单讲一下,yield后面的值可以简单理解为每一次return回的数据。只不过这个‘return’和之前的函数return不一样。

使用Generator对象

因为yield函数返回的是一个生成器Generator对象,所有。自然应该使用原生的Generator对象去操作。

对象如下Generator implements Iterator {

/* Methods */

public current ( void ) : mixed //获取当前yield的值

public getReturn ( void ) : mixed //获取函数的return值

public key ( void ) : mixed

public next ( void ) : void //唤醒生成器执行

public rewind ( void ) : void //重置迭代器

public send ( mixed $value ) : mixed //中途发送数据给yield

public throw ( Throwable $exception ) : mixed

public valid ( void ) : bool //验证是否已经被关闭了

public __wakeup ( void ) : void

}

然后我们用生成器来调用一下我们刚才的那个yieldNum函数。写起来就像下面这这个样子,确实没有foreach来的简洁。但好处是它更灵活,便于我们以后对生成器进行操作。$g = yieldNum(); //获取生成器

while($g->valid()){ //看看是否还可用

echo $g->current()."n"; //获取当前的yield返回的值

$g->next(); //移到下一步

}

与之交互

如果仅仅能实现在函数的某个位置中断,显然还不够。我们希望不仅中断,还能在中断恢复的时候与之进行交互。也就是说,我们希望,下一次启动的时候,我们可以自己传一些值给他。

如果我们仔细看一下Generator对象的实现,会发现有一个send方法。这个方法就是可以在函数再次启动的时候,赋值给它。

我们来一个例子,假设有一个奇怪的函数BlackBox,它是一个生成器,每一次都会随机输出一个0~100的数字function BlackBox(){

while(true){

yield mt_rand(0,100);

}

}

然后我们调用它$g = BlackBox();

echo $g->current()."n";

$g->next();

echo $g->current()."n";

会发现,打印出了2个随机数37

65

好了,然后我们现在有一个需求,希望我们可以随意调整最大值,就是说,再一次我们希望返回(0,100)的随机数,下一次我希望这个函数返回(0,1000)的随机数。这要怎么去实现呢?这就是send方法的用武之地了。

我们重新修改一下我们的代码function BlackBox(){

$max = 100;

while(true){

$temp = (yield mt_rand(0,$max));

if($temp != null){

$max = $temp;

}

}

}

先来简单解释一下这个代码。第一声明了一个$max变量,用于存储最大值,默认为100。然后不断循环。接着代码执行到yield mt_rand(0,$max)这个语句,程序会调用mt_rand生成一个(0,$max)的随机数,并暂停然后返回随机数。

接下来的$temp = (yield mt_rand(0,$max));才是重头戏,右边由括号包裹,表示等代码重新启动运行的时候,右边的表达式会返回一个值(有send方法传输过去的),这个值会保存到变量$temp里面。

如果temp变量里的值是null,则表示send方法没有传输值,又或者使用的是next方法。

题外话,next方法其实就是send(null)的一种实现

最后,判断一下temp变量里的值是否为null,如果不为空,则表示send了最大值,把这个值赋值给max变量,接下来再执行一次循环,代码暂停在yield mt_rand(0,$max)。等待下一次调用current方法去获取值。

然后我们来看看调用方法$g = BlackBox();

echo $g->current()."n";

$g->next();

echo $g->current()."n";

$g->send(10000);

echo $g->current()."n";

程序返回8

53

1574

可以很明显的看出来,第一次第二次的随机数都是在100以内的,而第三次的随机数是10000以内的。我们的代码成功了!

总结

OK,以上就是yield简单的使用方法。了解yield对我们进行了的文章很有帮助,因为我们的协程就是需要用到yield的这种特型来实现。如果有兴趣可以看看yield相关的教程。接下来的文章我们会继续走我们的PHP协程之路,让我们拭目以待吧~

注意:本文未经许可,不得转载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值