首先,这是第一次把公众号文章复制粘贴到sf.gg。其次,很久很久以前,我挖了一个坑来换取收益,把自己挖的坑填上,否则我迟早会把自己埋掉。最后,如果想看之前的坑,请把“yield”发到文末的公众号。我开通了搞大的自动回复功能,难得!PS:我在那篇文章的最后犯了一个错误,得出了一个错误的结论:send不能用在foreach中,猜测这是PHP的一个bug。事实上,它不是。真正的原因是send会产生处理器继续执行一次造成的。这件事告诉我们:扔锅除了装逼之外,也是一个有打脸风险的坑。内容和百度能搜到的大部分文章差不多,不过我坑的标题Goodstart:《yield是个什么玩意(上)》,意思是大家还有下一章,所以起标题也需要一定的技术含量。坚信看完上篇坑文,在座各位最想说的就是泰迪熊的那句话(这是文化属性,不以你的意志为转移):回到今天的主题,强调几个要点:虽然文章标题中有“yield与协程”等关键词,但实际上yield并不是协程。好像很多人直接把yield和coroutine等同起来。yield的本质是一个生成器,它的英文名字是Generator。yield只能用在function上,但是使用yield已经不是传统意义上的function了。同时,如果你试图在function之外的其他地方使用yield,你就会被打脸。yield最重要的作用就是自己打断一堆代码的执行,然后主动把CPU控制权让给路人A;然后从以某种方式中断的地方恢复操作。这很酷。如果请求一个服务端API,耗时10秒,此时可以把CPU让给路人A。粗略地说,以上过程就是协程的基本概念。多线程和多处理都是由操作系统参与调度,而协程是由用户自主调度的。协程的重点其实是“用户层自主调度”,大概意思就是“翻农奴唱吧”。让我用一堆代码体验一下“翻身农奴”。你可以感受一下:current();//现在我可以让task2介入echo$task2->current();//task1恢复中断$task1->next();//task2恢复中断$task2->next();}上面代码的执行结果如下图所示:虽然我这里都说了,但是肯定还有人看不懂“所以,发生了什么?”。要知道如果函数gen1和函数gen2中没有yield,而是普通函数,是不能打断for循环的,比如下面的代码:($i=1;$i<=10;$i++){echo"GEN1:{$i}".PHP_EOL;睡眠(1);}}functiongen2(){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利用率最大化。此代码段仅用于demo0);$ret=curl_multi_getcontent($ch1);echo$ret.PHP_EOL;returnfalse;}functiongen2(){for($i=1;$i<=10;$i++){echo"gen2:{$i}".PHP_EOL;file_put_contents("./yield.log","gen2".$i,FILE_APPEND);屈服;}}$gen1=gen1($mh,$ch1);$gen2=gen2();while(true){echo$gen1->current();echo$gen2->当前();$gen1->下一个();$gen2->next();}运行上面的代码后,我们等待curl在5秒内发起请求,同时可以完成写文件功能。做通常的PHP程序,只能阻塞等待curl得到结果,才能完成文件写入。文章太长了,就像“老太太的裹脚布,又臭又长”,所以,最后,对代码做一个非常小的改动,最后就不再写了!此代码段仅用于demo0);$ret=curl_multi_getcontent($ch1);echo$ret.PHP_EOL;returnfalse;}functiongen2(){for($i=1;$i<=10;$i++){echo"gen2:{$i}".PHP_EOL;file_put_contents("./yield.log","gen2".$i,FILE_APPEND);$rs=产量;echo"外部发送数据{$rs}".PHP_EOL;}}$gen1=gen1($mh,$ch1);$gen2=gen2();while(true){echo$gen1->current();echo$gen2->current();$gen1->发送("gen1");$gen2->send("gen2");}我们修改了内容:$gen1->next()改为$gen1->send("gen1")在函数gen1中,yield是有返回值的,打印出返回值的事实告诉我们:yield和send可以双向通信,同时告诉我们send可以用来恢复原来的样子打断了代码,而且在recovery的时候打断的时候可以把信息背回去写到这里,你觉得这个东西的可用性值是不是比以前高了?我知道,肯定有人说:“老李,你的代码真辣.鸡!你之前承诺过的---只在公司的生产环境写辣鸡代码。不过看你辣鸡光环,都在demo里盖了,你demo都不放过!你怎么说?!”.dei大哥,“不是不能用。”还有告诉你,上面的curldemo对yield的理解还不够,不要看,劝你放轻松,想想你的坏处代码,还有什么不能体验的呢?文末小故事:其实PHP5.5已经加入了yield,这个模块的作者叫NikitaPopov,网上的名字叫Nikic.我们知道PHP7的主力是惠新臣,而下一代PHP的主力是Nikic,早在2012年Nikic就发表了一篇关于PHPyieldmultitasking的文章,链接我贴出来大家欣赏---http://nikic.github.io/2012/1...-----分割线----重要提示重要提示重要提示:前面说到,使用yield可以暂时放弃CPU.在demo中,我们使用了curlmultiexec()函数来解释让出CPU的特性,但是这里需要注意,在这种情况下,不要使用具有阻塞属性的函数(其实PHP的大部分功能都直接灭绝了,只能通过尝试组合类似epoll的解决方案来进一步开发。包括本文demo中使用的curlmultiselect(),最好用curlmultiselect(),其实是最好的,而且curlmultiselect()的底层是调用selectIOmultiplexing)。之前跟一些博主在demo代码段也觉得多余【本demo仅做demo使用】。现在我觉得以后还是小心点为好。这个提醒来自文章中的一条评论。不能忍受背黑锅【误入歧途的孩子】,但文章确实缺少这个重要提醒。这是我的疏忽。非常感谢。
