Home > Article > Backend Development > PHP implementation of coroutines_PHP tutorial
In server programming, in order to achieve asynchronous implementation, callback functions are often needed, such as the following code
function send($value) { $data = process($value); onReceive($data); } function onReceive($recv_value) { var_dump($recv_value); } function process($value) { return $value+1; } $send_value = 1; send($send_value);
What is implemented is very simple. In fact, it is to send send_value to the remote end. The remote server adds one to it and then sends it back. So in onReceive we can get the return value recv_value of the remote server.
But such code will look fragmented, especially when remote procedure calls are made again in the process, and it will become more difficult to develop and maintain. Coroutines are designed to solve this problem and make asynchronous code look synchronous.
The following is an example of using PHP's yield to complete code scheduling (if you want to understand this code, you need to first understand the new features of PHP 5.5, generator and yield)
The frame code is as follows:
class CCoroutine { /** * * @var Generator */ public $coroutine; /** * * @var miexed null or CCoroutine */ public $father; public function __construct($coroutine, $father = null) { $this->coroutine = $coroutine; $this->father = $father; } } class AsyncTask { public $data; public function __construct($data) { $this->data = $data; } } abstract class CoroutineScheduler { protected $coroutine = NULL; abstract function send_and_receive($value); public function run($data) { $co = $this->send_and_receive($data); $ccoroutine = new CCoroutine($co); $this->schedule($ccoroutine); } protected function schedule($ccoroutine) { $task = $ccoroutine->coroutine->current(); //如果当前值为空,表示这个$ccoroutine应该已经结束了 if (is_null($task)) { if (is_null($ccoroutine->father)) { //已经彻底调度结束了--一般是onRecieve方法运行到最后一步了 return; } else { //注意,如果运行到这个分支,则表示子生成器没有给父生成器传数据 //子生成器可能是通过引用传递来改变父生成器的变量值 //所以这个时候只要调度父生成器就可以了 $ccoroutine->father->coroutine->next(); $father = $ccoroutine->father; $this->schedule($father); unset($ccoroutine); } } else { if (is_object($task) && $task instanceof AsyncTask) { //当task是异步数据请求的时候,开始处理socket并且将进程熄火在这里 $this->dealTask($task, $ccoroutine); } elseif (is_object($task) && $task instanceof \Generator) { //当task是生成器时,表示当前生成器又有了子生成器的调用 $newcc = new CCoroutine($task, $ccoroutine); $this->schedule($newcc); } elseif ($ccoroutine->father != null) { //注意,如果运行到这个分支,则表示在子的生成器里调用了yield $str;这样的写法 //我们规定这种写法是在给父生成器传数据,所以当前生成器就会终止调用了转而去调度父生成器 $ccoroutine->father->coroutine->send($task); $father = $ccoroutine->father; $this->schedule($father); unset($ccoroutine); } } } protected function dealTask($task, $ccoroutine) { $this->coroutine = $ccoroutine; $this->send($task->data); } public function send($value) { $data = $this->process($value); $this->onReceive($data); } public function process($value) { return $value+1; } protected function onReceive($data) { $this->coroutine->coroutine->send($data); $this->schedule($this->coroutine); } }
The caller code is as follows:
//1. 需要去实现CoroutineScheduler的send_and_receive函数,主要是为了在里面拿到返回值 class Solution extends CoroutineScheduler { public function send_and_receive($data) { $result = (yield new AsyncTask($data)); var_dump($result); } } //2. 在最外层去调用框架的代码,给出输入参数 $data $s = new Solution(); $data = 1; $s->run($data);