所以你在一个协程函数中嵌套另外一个协程函数:
<?php
function echoTimes($msg, $max) {
for ($i = 1; $i <= $max; ++$i) {
echo "$msg iteration $i\n";
yield;
}
}
function task() {
echoTimes('foo', 10); // print foo ten times
echo "---\n";
echoTimes('bar', 5); // print bar five times
yield; // force it to be a coroutine
}
$scheduler = new Scheduler;
$scheduler->newTask(task());
$scheduler->run();
这里的echoTimes是执行不了的!所以就需要协程堆栈。
不过没关系,我们改一改我们刚刚的代码。
把Task中的初始化方法改下,因为我们在运行一个Task的时候,我们要分析出他包含了哪些子协程,然后将子协程用一个堆栈保存。(C语言学的好的同学自然能理解这里,不理解的同学我建议去了解下进程的内存模型是怎么处理函数调用)
/**
* Task constructor.
* @param $taskId
* @param Generator $coroutine
*/
public function __construct($taskId, Generator $coroutine)
{
$this->taskId = $taskId;
// $this->coroutine = $coroutine;
// 换成这个,实际Task->run的就是stackedCoroutine这个函数,不是$coroutine保存的闭包函数了
$this->coroutine = stackedCoroutine($coroutine);
}
当Task->run()的时候,一个循环来分析:
/**
* @param Generator $gen
*/
function stackedCoroutine(Generator $gen)
{
$stack = new SplStack;
// 不断遍历这个传进来的生成器
for (; ;) {
// $gen可以理解为指向当前运行的协程闭包函数(生成器)
$value = $gen->current(); // 获取中断点,也就是yield出来的值
if ($value instanceof Generator) {
// 如果是也是一个生成器,这就是子协程了,把当前运行的协程入栈保存
$stack->push($gen);
$gen = $value; // 把子协程函数给gen,继续执行,注意接下来就是执行子协程的流程了
continue;
}
// 我们对子协程返回的结果做了封装,下面讲
$isReturnValue = $value instanceof CoroutineReturnValue; // 子协程返回`$value`需要主协程帮忙处理
if (!$gen->valid() || $isReturnValue) {
if ($stack->isEmpty()) {
return;
}
// 如果是gen已经执行完毕,或者遇到子协程需要返回值给主协程去处理
$gen = $stack->pop(); //出栈,得到之前入栈保存的主协程
$gen->send($isReturnValue ? $value->getValue() : NULL); // 调用主协程处理子协程的输出值
continue;
}
$gen->send(yield $gen->key() => $value); // 继续执行子协程
}
}
然后我们增加echoTime的结束标示:
class CoroutineReturnValue {
protected $value;
public function __construct($value) {
$this->value = $value;
}
// 获取能把子协程的输出值给主协程,作为主协程的send参数
public function getValue() {
return $this->value;
}
}
function retval($value) {
return new CoroutineReturnValue($value);
}
然后修改echoTimes:
function echoTimes($msg, $max) {
for ($i = 1; $i <= $max; ++$i) {
echo "$msg iteration $i\n";
yield;
}
yield retval(""); // 增加这个作为结束标示
}
Task变为:
function task1()
{
yield echoTimes('bar', 5);
}
这样就实现了一个协程堆栈,现在你可以举一反三了。
4)PHP7中yield from关键字
PHP7中增加了yield from,所以我们不需要自己实现携程堆栈,真实太好了。
把Task的构造函数改回去:
public function __construct($taskId, Generator $coroutine)
{
$this->taskId = $taskId;
$this->coroutine = $coroutine;
// $this->coroutine = stackedCoroutine($coroutine); //不需要自己实现了,改回之前的
}
echoTimes函数:
function echoTimes($msg, $max) {
for ($i = 1; $i <= $max; ++$i) {
echo "$msg iteration $i\n";
yield;
}
}
task1生成器:
function task1()
{
yield from echoTimes('bar', 5);
}
这样,轻松调用子协程。
总结
这下应该明白怎么实现PHP协程了吧?
PHP有序表查找之插值查找算法示例这篇文章主要介绍了PHP有序表查找之插值查找算法,简单分析了插值查找算法的概念、原理并结合实例形式分析了php实
ThinkPHP整合datatables实现服务端分页的示例代码下面小编就为大家分享一篇ThinkPHP整合datatables实现服务端分页的示例代码,具有很好的参考价值,希望对大家有所帮
PHP实现APP微信支付的实例讲解下面小编就为大家分享一篇PHP实现APP微信支付的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小
PHP实现的多维数组排序算法分析这篇文章主要介绍了PHP实现的多维数组排序算法,结合实例形式对比分析了php针对多维数组及带有键名的多维数组进行
php+ajax实现无刷新文件上传功能(ajaxuploadfile)这篇文章主要为大家详细介绍了php结合ajaxuploadfile实现无刷新文件上传功能,具有一定的参考价值,感兴趣的小伙伴们
PHP的RSA加密解密方法以及开发接口使用本篇文章给大家详细介绍了PHP开发接口使用RSA进行加密解密方法,对此有兴趣的朋友可以学习下。