你好, 我是程序猿零壹。******
******今天要给大家带来php中yield的用法。对于yield,我相信大部分的人依旧是不会用,甚至不知道什么是yield。那么今天这篇文章就是来告诉大家有关于yield的一些用法,以及如何用yield来解决我们在php中遇到的问题。希望对大家有所帮助。******
******不知道大家有没有碰到过这样的问题,使用excel导入大量数据的时候会失败,并且提示以下错误信息:******
Fatal Error: Allowed memory size of xxxxxx bytes
******这个是因为在php程序中,是将变量存储在内存中。当从excel中要导入的数据量过大的时候,会出现内存不足的错误提示。
要解决这个问题,可以通过修改php中对于最大运行内存的设置:
ini_set('memory_limit', '200M');
但是这么做不能从根本上解决问题,当我们需要读取5g甚至更大文件的时候,我们的运行内存可能就吃不消了。
幸好,在php5.5之后提新增了生成器(Generators)特性,用于简化实现迭代器接口(iterator)创建简单的迭代器的复杂性。通过生成器,我们可以轻松的使用 foreach 迭代一系列的数据,而不需要事先在内存中构建要被迭代的对象,大大减少了内存的开销。
这样说可能比较抽象,不易于理解。所以我们先抛开有关于生成器的概念,先来看一个简单的例子。
$arr = range(1,100)
这里用到了range函数,它的作用是在内存中生成一个数组包含每个在指定范围内的值,并返回该数组。
如果我们自己来实现这样一个数组,应该怎么做呢?我们来看下面的代码:
function xrange($start,$end,$step=1){
$data = [];
for($i=$start;$i<$end,$i += $step) {
$data[] = $i;
}
return $data;
}
$start = memory_get_usage();
$data = xrange(1,1000);
foreach ($data as $value) {
echo $value.PHP_EOL;
}
$end = memory_get_usage();
echo "start:".$start.PHP_EOL;
echo "end:".$end.PHP_EOL;
echo "used:".($end - $start);
我们来看下start为1,end 分别为 10,100,1000,10000的情况下的内存消耗情况分别是怎么样的:
xrange(1,10); // used = 3480 0.0033187866210938MB
xrange(1,100); // used = 30168 0.028770446777344MB
xrange(1,1000); // used = 285144 0.27193450927734MB
xrange(1,10000); // used = 2957784 2.8207626342773MB
不难看出,随着$end的增大,所占用的内存也越来越大。
接下来我们来改造下xrange函数:
function xrange($start,$end,$step=1){
for($i=$start;$i<$end,$i += $step) {
yield $i;
}
}
我们删除了数组data,并且也删除了返回值,而在foreach的循环体里,在i前面添加关键字:yield。****
我们来看下改造之后的内存消耗:
xrange(1,10); // used = 256 0.000244140625MB
xrange(1,100); // used = 256 0.000244140625MB
xrange(1,1000); // used = 256 0.000244140625MB
xrange(1,10000); // used = 256 0.000244140625MB
Wow,这个结果令人惊讶。我们奇迹的发现了,内存消耗并没有随着$end的增大而增大,甚至是完全一样。
我们来还原一下代码的执行过程:
******首先调用xrange函数,传参$end=10,但是for循环了一次然后停止了,并且告诉foreach第一次循环可以用的值。
******foreach开始对$data循环,并使用for给的一个值执行输出。******
******foreach开始第二次循环,它向for循环又请求了一次******
******for循环又执行了一次,并将新的值告诉foreach
******foreach拿到第二个值,开始输出。
******所以,整个代码执行中,始终只有一个记录值参与循环,内存中也只有一条信息。
无论开始传入按的$end有多大,由于不会立即生成所有结果集,所以内存始终是一条循环的值,也就不会占用太大的内存了。
******看到这里,你是不是想说,“就这?”。生成器的用处当然不止这一些,还有其他的用武之地,比如协程。只不过因为本人才疏学浅,只能跟大家分享这么多了。大家感兴趣的话,可以看下鸟哥关于在php中使用协程实现多任务调度的文章。
好了,今天就到这里,如果大家觉得有用的话,不要忘记点赞收藏哦~
全部0条评论
快来发表一下你的评论吧 !