内容简介:首先是,这是我第一次把公众号文章复制粘贴到sf.gg来。其次是,很久很久之前,我挖了一个yield的一个坑,自己挖的坑自己填,不然迟早会把自己埋掉。最后是,如果想看之前那个坑,请发送“yield”给文章末尾的公众号,我开通了高大上的自动回复功能,稀罕地不得了!
首先是,这是我第一次把公众号文章复制粘贴到sf.gg来。
其次是,很久很久之前,我挖了一个yield的一个坑,自己挖的坑自己填,不然迟早会把自己埋掉。
最后是,如果想看之前那个坑,请发送“yield”给文章末尾的公众号,我开通了高大上的自动回复功能,稀罕地不得了!
PS:那篇文章中在最后我犯了一个错误,误下了一个结论:foreach中不能使用send并猜测这是 PHP 的bug,实际上并不是,真实的原因粗暴简单的理解就是send会让生成器继续执行一次导致。这件事情告诉我们:
除了装逼之外,甩锅也是有打脸风险的
那篇坑里,内容和你能在百毒上搜索到的大多数文章都是差不多的,不过我那篇坑标题起得好:《yield是个什么玩意(上)》,也就是暗示大家还有下篇,所以起标题也是需要一定技术含量的。
我坚信,在座的各位辣鸡在看完上篇坑文后最想说的注定是泰迪熊这句话(这是文化属性,不以各位的意志而转移):
回到今天主旨上来,强调几点:
- 虽然文章标题中有“yield和协程”这样的关键字,但实际上yield并不是协程,看起来有不少人直接将yield和协程划了等号。yield的本质是生成器,英文名字叫做Generator。
- yield只能用在function中,但用了yield就已经不是传统意义上的function了,同时如果你企图在function之外的其他地方用yield,你会被打脸。
- yield的最重要作用就是:自己中断一坨代码的执行,然后主动让出CPU控制权给路人甲;然后又能通过一些方式从刚才中断的地方恢复运行。这个就比较屌了,假如你请求了一个费时10s的服务器API,此时是可以让出CPU给路人甲。粗暴地说上面的过程就算是协程的基本概念。
多线程和多进程都是操作系统参与的调度,而协程是用户自主实现的调度,协程的关键点实际上是“用户层实现自主调度”,大概有“翻身农奴把歌唱”的意思。
下面我通过一坨代码来体会一把“翻身农奴”,你们感受一下:
<?php
function gen1() {
for( $i = 1; $i <= 10; $i++ ) {
echo "GEN1 : {$i}".PHP_EOL;
// sleep没啥意思,主要就是运行时候给你一种切实的调度感,你懂么
// 就是那种“你看!你看!尼玛,我调度了!卧槽”
sleep( 1 );
// 这句很关键,表示自己主动让出CPU,我不下地狱谁下地狱
yield;
}
}
function gen2() {
for( $i = 1; $i <= 10; $i++ ) {
echo "GEN2 : {$i}".PHP_EOL;
// sleep没啥意思,主要就是运行时候给你一种切实的调度感,你懂么
// 就是那种“你看!你看!尼玛,我调度了!卧槽”
sleep( 1 );
// 这句很关键,表示自己主动让出CPU,我不下地狱谁下地狱
yield;
}
}
$task1 = gen1();
$task2 = gen2();
while( true ) {
// 首先我运行task1,然后task1主动下了地狱
echo $task1->current();
// 这会儿我可以让task2介入进来了
echo $task2->current();
// task1恢复中断
$task1->next();
// task2恢复中断
$task2->next();
}
上面代码执行结果如下图:
虽然我话都说到这里了,但是肯定还是有人get不到“所以,到底发生了什么?”。你要知道,如果function gen1和function gen2中没有yield,而是普通函数,你是无法中断其中的for循环的,诸如下面这样的代码:
<?php
function gen1() {
for( $i = 1; $i <= 10; $i++ ) {
echo "GEN1 : {$i}".PHP_EOL;
sleep( 1 );
}
}
function gen2() {
for( $i = 1; $i <= 10; $i++ ) {
echo "GEN2 : {$i}".PHP_EOL;
}
}
gen1();
gen2();
// 看这里,看这里,看这里!
// 上面的代码一旦运行,一定是先运行完gen1函数中的for循环
// 其次才能运行完gen2函数中的for循环,绝对不会出现
// gen1和gen2交叉运行这种情况
我似乎已然精通了yield
写到这里后我也开始蹩了,和以往的憋了三天蹦不出来个屁有所不同,我这次蹩出了一个比较典型的应用场景:curl。下面我们基于上面那坨辣鸡代码将gen1修改为一个耗时curl网络请求,gen2将向一个文本文件中写内容,我们的目的就是在耗时的curl开始后主动让出CPU,让gen2去写文件,以实现CPU的最大化利用。
<?php
$ch1 = curl_init();
// 这个地址中的php,我故意sleep了5秒钟,然后输出一坨json
curl_setopt( $ch1, CURLOPT_URL, "http://www.selfctrler.com/index.php/test/test1" );
curl_setopt( $ch1, CURLOPT_HEADER, 0 );
$mh = curl_multi_init();
curl_multi_add_handle( $mh, $ch1 );
function gen1( $mh, $ch1 ) {
do {
$mrc = curl_multi_exec( $mh, $running );
// 请求发出后,让出cpu
yield;
} while( $running > 0 );
$ret = curl_multi_getcontent( $ch1 );
echo $ret.PHP_EOL;
return false;
}
function gen2() {
for ( $i = 1; $i <= 10; $i++ ) {
echo "gen2 : {$i}".PHP_EOL;
file_put_contents( "./yield.log", "gen2".$i, FILE_APPEND );
yield;
}
}
$gen1 = gen1( $mh, $ch1 );
$gen2 = gen2();
while( true ) {
echo $gen1->current();
echo $gen2->current();
$gen1->next();
$gen2->next();
}
上面的代码,运行以后,我们再等待curl发起请求的5秒钟内,同时可以完成文件写入功能,如果换做平时的PHP程序,就只能是先阻塞等待curl拿到结果后才能完成文件写入。
文章太长,就像“老太太的裹脚布一样,又臭又长”,所以,最后再对代码做个极小幅度的改动就收尾不写了!
<?php
$ch1 = curl_init();
// 这个地址中的php,我故意sleep了5秒钟,然后输出一坨json
curl_setopt( $ch1, CURLOPT_URL, "http://www.selfctrler.com/index.php/test/test1" );
curl_setopt( $ch1, CURLOPT_HEADER, 0 );
$mh = curl_multi_init();
curl_multi_add_handle( $mh, $ch1 );
function gen1( $mh, $ch1 ) {
do {
$mrc = curl_multi_exec( $mh, $running );
// 请求发出后,让出cpu
$rs = yield;
echo "外部发送数据{$rs}".PHP_EOL;
} while( $running > 0 );
$ret = curl_multi_getcontent( $ch1 );
echo $ret.PHP_EOL;
return false;
}
function gen2() {
for ( $i = 1; $i <= 10; $i++ ) {
echo "gen2 : {$i}".PHP_EOL;
file_put_contents( "./yield.log", "gen2".$i, FILE_APPEND );
$rs = yield;
echo "外部发送数据{$rs}".PHP_EOL;
}
}
$gen1 = gen1( $mh, $ch1 );
$gen2 = gen2();
while( true ) {
echo $gen1->current();
echo $gen2->current();
$gen1->send("gen1");
$gen2->send("gen2");
}
我们修改了内容:
将$gen1->next()修改成了$gen1->send("gen1")
在function gen1中yield有了返回值,并且将返回值打印出来
这件事情告诉我们:yield和send,是可以双向通信的,同时告诉我们send可以用来恢复原来中断的代码,而且在恢复中断的同时可以携带信息回去。写到这里,你是不是觉得这玩意的可利用价值是不是比原来高点儿了?
我知道,有人肯定叨叨了:“老李,你代码特么写的真是辣鸡啊!你之前保证过了的 --- 只在公司生产环境写辣鸡代码的。可你看看你这辣鸡光环到笼罩都到demo里了,你连demo都不放过了!你怎么说?!”。兄dei,“又不是不能用”。而且我告诉你,上面这点儿curl demo来讲明白yield还是不够的,后面还有两三篇yield呢,照样是烂代码恶心死你,爱看不看。我劝你心放宽,你想想你这么烂的代码都经历了,还有什么不能经历的?
文章最后补个小故事:其实yield是PHP 5.5就已经添加进来了,这个模块的作者叫做Nikita Popov,网络上的名称是Nikic。我们知道PHP7这一代主力是惠新宸,下一代PHP主力就是Nikic了。早在2012年,Nikic就发表了一篇关于PHP yield多任务的文章,链接我贴出来大家共赏一下 --- http://nikic.github.io/2012/1...
最近开了一个微信公众号,所有文章都在这里(手贱弄成服务号了)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 在DJ的日子
- 在酷家乐做面试官的日子
- 程序员练英语听力的那些日子
- Python爬虫教程:爬取付费电影,告别费钱的日子
- 互联网大厂复工的日子:收到几万元工资很欣喜
- 离开互联网公司的日子:当程序员不如送外卖?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PHP for the World Wide Web, Second Edition (Visual QuickStart Gu
Larry Ullman / Peachpit Press / 2004-02-02 / USD 29.99
So you know HTML, even JavaScript, but the idea of learning an actual programming language like PHP terrifies you? Well, stop quaking and get going with this easy task-based guide! Aimed at beginning ......一起来看看 《PHP for the World Wide Web, Second Edition (Visual QuickStart Gu》 这本书的介绍吧!