“协程”就是用户态的线程
要理解是什么是“用户态的线程”,必然就要先理解什么是“内核态的线程”。 内核态的线程是由操做系统来进行调度的,在切换线程上下文时,要先保存上一个线程的上下文,而后执行下一个线程,当条件知足时,切换回上一个线程,并恢复上下文。 协程也是如此,只不过,用户态的线程不是由操做系统来调度的,而是由程序员来调度的,是在用户态的 -- 摘自连接描述php
咱们有两个函数 task1
,task2
,咱们来手动调度它们的执行顺序,好比在task1
执行一半的时候去执行task2
,两个或者多个函数之间交替执行(这就是协程
的概念)。程序员
咱们来个正常的函数调用方式:shell
<?php function task1() { echo "task1函数 执行1\n"; echo "task1函数 执行2\n"; } function task2() { echo "task2函数 执行1\n"; echo "task2函数 执行2\n"; } // 调度 task1(); task2();
可想而知,以上的输出确定是:segmentfault
task1函数 执行第1 task1函数 执行第2 task2函数 执行第1 task2函数 执行第2
可是我想在程序输出task1函数 执行1
以后就输出task2函数 执行1
怎么办?网络
这个时候 yield 就派上用场了,PHP
里的协程是须要借助 yield 来完成的。记住,yield 不是协程,而是协程
须要借助 yield 的特性来实现。函数
<?php function task1() { echo "task1函数 执行1\n"; yield; echo "task1函数 执行2\n"; } function task2() { echo "task2函数 执行1\n"; yield; echo "task2函数 执行2\n"; } // 调度 $task1 = task1(); // 返回一个生成器 $task2 = task2(); // 返回一个生成器 $task1->current(); $task2->current();
以上输出:oop
task1函数 执行1 task2函数 执行1
很好,以上结果达到了咱们的预期。可是怎么让函数里的代码往下执行呢?编码
调用生成器的next
方法:操作系统
$task1->next(); $task2->next();
最后你将看到的输出结果是两个函数交替执行输出的:.net
task1函数 执行1 task2函数 执行1 task1函数 执行2 task2函数 执行2
以上的代码实现能够抽象出两个概念,任务
和调度
,任务
就是task函数,调度
就是咱们怎么去调用这些task函数
上一个小段总结里有两个概念叫任务
和调度
,咱们简单的封装个任务生成器和调度器
// 任务生成器 $createTask = (function () { $tasks = []; return function ($callback) use (&$tasks) { $task = [ 'task' => $callback(), 'id' => count($tasks) + 1, ]; array_push($tasks, $task); return $task; }; })(); // 调度器 function schedule($tasks) { $first = []; while (!empty($tasks)) { $task = array_shift($tasks); if (!array_key_exists($task['id'], $first)) { $first[$task['id']] = true; $task['task']->current(); } else { $task['task']->next(); } if (!$task['task']->valid()) { unset($tasks[$k]); } else { array_push($tasks, $task); } } }
使用
$tasks = [ $createTask(function () { echo "任务1 执行第1次\n"; yield; echo "任务1 执行第2次\n"; }), $createTask(function () { echo "任务2 执行第1次\n"; yield; echo "任务2 执行第2次\n"; }) ]; schedule($tasks);
输出结果:
任务1 执行第1次 任务2 执行第1次 任务1 执行第2次 任务2 执行第2次
能够从结果看出,调度器已经实现了多个任务之间进行协做。
如今有个需求!就是任务在遇到网络请求的时候,咱们无需等待网络请求的响应结果,而是遇到网络请求的时候,把这个任务挂起,而后去执行其它任务,等网络请求收到响应结果了再通知咱们处理
这时候须要咱们用到非阻塞IO调用
相关技术,涉及到系统内核层面,想了解能够点击连接描述
在PHP里咱们须要安装个扩展eio
,你们自行安装
pecl install eio
编码:
$tasks = [ $createTask(function () { echo "任务1 执行第1次\n"; yield; echo "任务1 执行第2次\n"; }), $createTask(function () { echo "任务2 执行第1次\n"; eio_custom(function () { return file_get_contents('https://segmentfault.com/'); }, EIO_PRI_DEFAULT, function ($data, $ret) { echo "请求完成\n"; }); yield; echo "任务2 执行第2次\n"; }) ]; schedule($tasks); eio_event_loop();
在任务2 执行第1次
的时候,遇到网络请求,咱们把请求任务交给系统内核,而后切换到其它任务去,等请求任务完成后回调咱们传入的函数。
输出结果:
任务1 执行第1次 任务2 执行第1次 任务1 执行第2次 任务2 执行第2次 任务2 执行第1次的请求完成
完!