yield :PHP大数据量处理中的内存优化神器

相信很多PHP开发者都没接触过yield 这个关键词,一方面这个功能出来的比较晚,在PHP的5.5版本中才出现;其次应用面不是很广,在平时的业务代码中不太会去使用;最后就是PHP开发者对于进程,线程和协程的概念不是太清楚,习惯写同步代码。

yield的定义

生成器函数的核心是yield关键字。它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。

function gen_one_to_three() {
   for ($i = 1; $i <= 3; $i++) {
     //注意变量$i的值在不同的yield之间是保持传递的。
     yield $i;
   }
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
   echo "$value\n";
}

以上是官方文档的定义,有点晦涩难懂。接下来我会说明下我理解中的yield.

yield的理解

首先写一段我们常见的代码

function demo(int $count = 5){
   for ($i=0; $i<$count; $i++){
      $data[] = time();
   }
   return $data;
}
$result = demo();
foreach ($result as $item){
   sleep(1); 
   echo $item.PHP_EOL;
}

打印结果如下

 1542623418
 1542623418
 1542623418
 1542623418
 1542623418

很正常,没什么疑问,一次性算出所有的数据放到数组中存放起来,因为代码跑的很快,几乎在同时就计算完了,所以数组中的值都一样,依次打印出来也是相同的。
接下来看下用yield改造之后的代码:

function demo(int $count = 5){
   for ($i=0; $i<$count; $i++){
      yield time();
   }
}
$result = demo();
foreach ($result as $item){
   sleep(1);
   echo $item.PHP_EOL;
}

去掉了用数组存值并返回的部分,用yield 来代替了,那打印的结果如何呢?结果如下:

1542623567
1542623568
1542623569
1542623570
1542623571

结果不一样了,时间都是间隔了一秒,这是因为在输出的时候sleep了一秒。
回头看下yield官方的说明:yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。也就是说demo 里面的数组并没有一次就全部计算完等待调用而是通过yield 形成了一个生成器,这个生成器会在每次调用的时候依次读取上次的运行状态并继续本次运行,然后暂时停止等待下次调用,生成器每次的值怎么计算的呢?就是demo方法里面的代码生成的。这个有协程的意思在里面。
举个例子:有一个txt文本,我们需要逐行的去读取里面的内容然后做处理。不用yield 就相当于一次性file_get_contents,读取到内存中来处理,而yield就是fgets来一行行的读取。因此可以知道yield在处理大数据量的情况下,可以通过分段的方式去逐步处理,减小内存的压力,当然数据量不大的情况下,还是一次性读取速度比较快。

对于yeild 协程的实现,大家可以去看看鸟哥的详解,在PHP中使用协程实现多任务调度,从这里可以看到自己与大佬的差距是有多大,还需要努力啊!