zan框架入门(一)——协程

zan 是基于PHP协程的网络服务框架,要使用zan框架,首先需要了解php的yield。建议先看一下 在PHP中使用协程实现多任务调度 这篇文章,基础部分内容都只是这篇文章的整理。
yield yield是php的生成器语法,放在函数内部使用,其效果是return值并且中断此函数的执行。
一个简单的生成值的例子


以上例程会输出
1 2 3

Generator
一个方法如果内部有yield语法,那么调用这个方法,就会返回一个Generator的对象

以上例程会输出
class Generator#1 (0) { }

查看php的官方手册,Generator的类摘要如下
Generator implements Iterator { /* 方法 */ public mixed current ( void ) public mixed key ( void ) public void next ( void ) public void rewind ( void ) public mixed send ( mixed $value ) public void throw ( Exception $exception ) public bool valid ( void ) public void __wakeup ( void ) }

Iterator
实现了Iterator接口的类,都可以用foreach循环来遍历
Iterator extends Traversable { /* 方法 */ abstract public mixed current ( void ) abstract public scalar key ( void ) abstract public void next ( void ) abstract public void rewind ( void ) abstract public boolean valid ( void ) }

协程 协程的支持是在迭代生成器的基础上, 增加了可以回送数据给生成器的功能。这就把生成器到调用者的单向通信转变为两者之间的双向通信。下面这个例子说明了如何同时进行接收和发送。
current()); // string(6) "yield1" var_dump($gen->send('ret1')); // string(4) "ret1"(the first var_dump in gen) // string(6) "yield2" (the var_dump of the ->send() return value) var_dump($gen->send('ret2')); // string(4) "ret2"(again from within gen) // NULL(the return value of ->send()) ?>

这里段代码看起来可能有点费解,调用send之前为什么需要先调用一次current,这个和send的机制有关
Generator::send 『如果当这个方法被调用时,生成器不在 yield 表达式,那么在传入值之前,它会先运行到第一个 yield 表达式。』,接着『向生成器中传入一个值,并且当做 yield 表达式的结果,然后继续执行生成器。』

多任务协作 了解了协程以后,我们就可以基于协程来实现多任务协作了。
多任务协作,有三个比较核心的概念:Task、Coroutine、Scheduler。
Task
首先看一下轻量级的task代码
taskId = $taskId; $this->coroutine = $coroutine; } public function getTaskId() { return $this->taskId; } public function setSendValue($sendValue) { $this->sendValue = https://www.it610.com/article/$sendValue; } public function run() { if ($this->beforeFirstYield) { $this->beforeFirstYield = false; return $this->coroutine->current(); } else { $retval = $this->coroutine->send($this->sendValue); $this->sendValue = https://www.it610.com/article/null; return $retval; } } public function isFinished() { return !$this->coroutine->valid(); } }

这是Task最简单的版本,只处理单个的yield方法。一个Task处理一个协程。
Scheduler
Scheduler就是调度器。顾名思义,就是调度任务用的。上代码:
task protected $taskQueue; public function __construct() { $this->taskQueue = new SplQueue(); } public function newTask(Generator $coroutine) { $tid = ++$this->maxTaskId; $task = new Task($tid, $coroutine); $this->taskMap[$tid] = $task; $this->schedule($task); return $tid; } public function schedule(Task $task) { $this->taskQueue->enqueue($task); } public function run() { while (!$this->taskQueue->isEmpty()) { $task = $this->taskQueue->dequeue(); $task->run(); if ($task->isFinished()) { unset($this->taskMap[$task->getTaskId()]); } else { $this->schedule($task); } } } } ?>

其实很简单,就是用队列实现了任务调度的功能而已。
参考资料 【zan框架入门(一)——协程】zan框架仓库
PHP: 生成器语法- Manual
PHP: 生成器 - Manual
Iterator(迭代器)接口
PHP SPL笔记
在PHP中使用协程实现多任务调度

    推荐阅读