想试试看,使用纯PHP代码,不依赖第三方扩展实现“多线程”?像Java一样使用setPriority()影响每个“线程”被调用的概率,使用join()等待其他线程结束;休眠时放弃CPU使用率,然后回到该点的“线程”;像Golang一样,使用channel协程之间通信~三部曲Yield语法探索yieldfrom语法探索yield实战“多线程”编码延续上一本书,说完yield的基本用法,这篇文章,带大家去实践,目标:手把手教你使用yield作为任务调度器来加深对PHP生成器的理解。yield的基本用法推荐大家去上一篇回顾一下。好了,话不多说,开始吧~上一讲,我们学习了如何使用function(){...yield...}将一个函数变成一个“生成器”,一个简单的任务调度器。它是一个简单的任务调度程序。代码比较少,直接贴在这里。gitee地址:./simpleYieldScheduler.phpgens[]=$gen;}else{$this->gens[$key]=$gen;}返回$this;}/***开始*/publicfunctionstart(){$keepRun=true;/***@var生成器$gen*/$gen=null;do{//循环调度任务foreach($this->gensas$id=>$gen){$re=$gen->current();echo'发电机编号:'。$身份证。'运行,获取当前回复:'。$重新。PHP_EOL;$gen->next();}//检查任务是否完成foreach($this->gensas$id=&g吨;$gen){$check=$gen->valid();if(!$check){//已经执行的任务可以踢出任务调度队列unset($this->gens[$id]);}}//调度器是否完成所有任务if(0>=count($this->gens)){$keepRun=false;}}while($keepRun);}}函数yieldFunc($max=10){for($i=0;$i<$max;$i++){(yield$i);}返回$i;}$gen1=yieldFunc(3);$gen2=yieldFunc(5);$scheduler=newYieldScheduler();$scheduler->add($gen1)->add($gen2);$scheduler->开始();运行结果:可以看到我们使用相同的方法,不同的输入参数生成了两个不同的生成器,一个生成器也是由另一个方法生成的。虽然生成方式不同,但并不影响三者一起启动,交替运行。它们的执行顺序是确定的(这个脚本运行的次数相同)Result)让我们彻底理解这一点。请参阅yieldFunc($max)函数。他写了一个循环,循环里面有一个yield。每当程序运行到这里,就会跳出当前函数,放弃runtime。创建三个生成器后,生成一个YieldScheduler对象,向其添加两个生成器,开始运行任务。在start()函数中,不断调用current和next方法来驱动generator运行。每次运行后都会调用valid检查生成器是否运行完毕。完成后,它将从任务调度器生成器队列中移除,将任务踢出。运行伪代码我会贴出代码执行序列的伪代码:current();//输入gen1$n=0;产量$n++;//跳出gen1,获取返回值赋值给$reecho'generatorid:'.$身份证。'运行,获取当前回复:'。$重新。PHP_EOL;$gen1->send($sum++)//sum=1//输入gen1$receive=yield;echo'发送调度程序:'。$接收。PHP_EOL;$n++;//跳出gen1//任务调度器检查任务是否完成if(!$gen1->valid()){unset($gen1);}if(empty($gens)){break;}//任务调度器进入第二个周期//开始调度第二个生成器$re=$gen2->current();//输入gen2,$i=0;如果($i<$max){yield$i;}//退出gen2echo'generatorid:'.$身份证。'运行,获取当前回复:'。$重新。PHP_EOL;$gen2->send($sum++)//sum=2//输入gen2$get=yield;echo'发送调度程序:'。$得到。PHP_EOL;$i++;如果($i<$max){返回$i;}//跳出gen2//任务调度器检查任务是否完成if(!$gen2->valid()){unset($gen2);}if(empty($gens)){break;}//任务调度器进入第三个循环//开始调度第三台发电机$re=$gen3->current();//进入gen3,这是第三个生成器,这个$i不是gen2的$i,所以$i从0开始$i=0;如果($i<$max){yield$i;}//跳出gen3echo'generatorid:'.$身份证。'运行,获取当前回复:'。$重新。PHP_EOL;$gen2->send($sum++)//sum=3//输入gen3$get=yield;echo'发送调度程序:'。$得到。PHP_EOL;$i++;如果($i<$max){返回$i;}//exitgen3//任务调度器检查任务是否完成if(!$gen3->valid()){unset($gen3);}if(empty($gens)){break;}//任务调度器进入第四个周期//再次开始调度第一个生成器$re=$gen1->current();//输入gen1yield$n;//$n=1,其中$n++在第一次调度中已经完成?//跳出gen1,获取返回值赋值给$reecho'generatorid:'.$身份证。'运行,获取当前回复:'。$重新。PHP_EOL;$gen1->send($sum++)//sum=4//输入gen1$receive=yield;echo'发送调度程序:'。$接收。PHP_EOL;$n++;//跳出gen1//任务调度器检查任务是否完成if(!$gen1->valid()){unset($gen1);}if(empty($gens)){break;}看看这段伪代码的执行顺序,你会想到什么?goto!,PHP也支持goto语法。为了代码阅读和维护方便,很少使用在代码执行到yieldd右边时跳出。这里有一个必须扣掉的细节,就是yield右边的表达式,或者函数执行完,会跳出当前生成器(指定yield这行代码时不会退出)。这个细节,你可以从yieldFunc和myPrint调用后的命令行输出中看到。在taskscheduler的第四次循环调度中,调用send()方法后,不仅echo'getschedulersent:'。$接收。PHP_EOL;,而且myPrint($n++)在生成器中执行。然后,是时候进入下一个生成器了。每个生成器(函数)中的变量都有自己的堆栈空间,不受其他生成器的影响。跳出当前生成器,变量的状态依然存在。这个地方有点像线程,每个线程也维护自己的栈空间。所以,你会看到$i=0,1,2...两者都打印了3次。线程有自己独占的栈内存和计数器。转载自著名出处:sifouPHP的goto,这里打断说说PHP.netgoto。PHP中的goto有一定的限制,目标位置只能位于同一个文件和作用域内,也就是说不能跳出一个函数或类方法,也不能跳出到另一个函数中。也没有办法跳入任何循环或切换结构。您可以跳出循环或开关。通常的用法是用goto代替多层断点。所以yield虽然不如goto灵活,但是比goto强大,可以跳循环,还可以跨函数跨作用域。好了,以上就是一个最简单的形态任务调度器,大家要看透了再继续往下看。一个更复杂的任务调度器一个更复杂的任务调度器,拿鸟哥转载的文章在PHP中使用协程实现多任务调度。文中讲一个任务调度器,文中迭代了2个版本。代码很多,代码散落在文章中。我把它们整理出来放到了gitee调度器中。您可以克隆以在本地运行。鸟哥的文章已经解释的很清楚了,我就不蛇添足了,说说个人感受吧。本文中的代码使用了大量闭包、回调和引用。很多地方都是一个一个传递可执行变量,理解起来有点费脑筋。对于多线程这样的任务调度器,我们先看看Java线程的生命周期和PHP生成器的状态图。有很多相似之处。接下来,我们尝试使用PHPyield来实现一个“类Java多线程”调度器。代码很多,放在gitee里。gitclonehttps://gitee.com/xupaul/PHP-...解释一下第一个Demo,priority$php./YieldBootstrap.php./YieldSchedulerDemo1.php这个测试代码使用了priority函数,可以看到t需要1周期,t2需要10个周期。由于t2的执行优先级最高,在随机调度过程中会很快执行。最后,t和t3(t3需要运行8个周期)最终被执行。第二个Demo,interrupt,sleep是按照Java实现的。当调用线程的中断方法时,会导致线程抛出异常。不过PHP的yield有个throw方法,于是照葫芦画瓢实现了。$php./YieldBootstrap.php./YieldSchedulerDemo1.php代码执行结果如下:YieldThread对象调用sleep方法时,5s内,任务调度输出不显示正在执行的“线程1”的输出。第三个Demo中join,wait,我代码中的join和wait是一个意思。等待线程执行完毕,但是join(seconds)的函数还没有执行完。$php./YieldBootstrap.php./YieldSchedulerDemo1.php执行效果如下在t3生成器中调用了t->join()后,t3没有调用完t才执行。而我们的主线程使用wait()等待他们t和t4都执行完了才开始输出他们执行完的字符。原理整个核心文件为:InterruptedException.phpMainYieldTread.phpYieldBootstrap.phpYieldThread.phpYieldThreadScheduler.php可以看到执行命令为:$php./YieldBootstrap.php./YieldSchedulerDemo1.php。php调用YieldBootstrap.php程序,将自定义代码(demo代码)作为参数传入。在引导程序中,将对主程序进行包装-MainYieldThread.php包装主生成器。自定义线程继承自YieldThread.php,主线程和自线程均继承自YieldThread,统一放入YieldThreadScheduler.php中进行统一调度,实现线程切换。这个“线程”的界面设计是照搬Java的,原理是根据Java-Thread生命周期图和PHP-yield活动状态图来实现的。对于任务调度,优先级采用轮盘赌,通过加入随机数实现随机调度。join和wait用一个数组记录线程间的依赖关系,判断当前线程是否就绪。这种多线程调度器并不是那么完美。后续更新将放在PHPyield线程中。结语话不多,代码很长,很辛酸。大家在本地下载,多运行,多琢磨。你一定会了解yieldAdvanced的用法。欢迎留言提问。参考https://www.runoob.com/java/j...https://www.jianshu.com/p/192...http://www.throwable.club/201...https://blog.csdn.net/u013087...https://m.php.cn/manual/view/...https://www.cnblogs.com/sundd...
