协程(Coroutine)(一)
协程(Coroutine)(一)
在使用easyswoole框架做开发的时候,经常会涉及到协程。作为从传统fpm模式转到swoole内存常驻模式的phper,还是有一点吃力的。但是有困难不应该退缩,我们要迎刃而上,要想把协程用好,就先要把它弄清楚。学习过程中参考了《easyswoole官方文档》和《swoole官方文档》。
一、概念
协程不是进程或线程,一个程序可以包含多个协程,可以对比于一个进程包含多个线程。多个线程相对独立,有自己的上下文,切换受系统控制;而协程也相对独立,有自己的上下文,但是其切换由自己控制。
因此,协程可以说就是用户态的线程。内核态的线程是由操作系统来进行调度的,但是用户态的线程不是由操作系统来调度的,而是由程序员来调度的,是在用户态的。
二、协程调度
用户的每个请求都会创建一个协程,请求结束后协程结束,如果同时有成千上万的并发请求,某一时刻某个进程内部会存在成千上万的协程,那么 CPU 资源是有限的,到底执行哪个协程的代码?决定到底让 CPU 执行哪个协程的代码决断过程就是协程调度
1)首先,在执行某个协程代码的过程中发现这行代码遇到了 Co::sleep() 或者产生了网络 IO,例如 MySQL->query(),这肯定是一个耗时的过程,Swoole 就会把这个 Mysql 连接的 fd(事件的句柄)放到 EventLoop 中
2)然后让出这个协程的 CPU 给其他协程使用:即 yield(挂起),等待 MySQL 数据返回后就继续执行这个协程:即 resume(恢复)
3)其次,如果协程的代码中有CPU密集型代码,我们可以开启enable_preemptive_scheduler,Swoole 会强行让这个协程让出 CPU。
三、协程的实现
1)yield生成器实现 (详细原理可查看http://www.php20.cn/article/148)
2)swoole扩展实现
说明:由于swoole的底层封装了协程,所以对比传统的 PHP 层协程框架,开发者不需要使用 yield 关键词来标识一个协程 IO 操作,所以不再需要对 yield 的语义进行深入理解以及对每一级的调用都修改为 yield,这极大的提高了开发效率
四、协程的生命周期
协程内的生命周期与普通php函数一致,当协程退出后,协程内变量将全部回收.
五、注意事项
1、协程环境
使用协程容器,才可以创建协程,否则没有协程环境,不能实现协程切换功能.不能用\co::sleep
2、变量使用
1)在协程中,需要特别注意,不要使用$_GET,$_POST,$GLOBALS等超全局变量,尤其是需要修改变量值并读取时,将造成协程间变量污染.
2)协程中访问外部变量必须使用use关键字,或者传形参方式,不能引用变量.
3)如果需要做多协程之间的通信,可使用channel方式通信.
3、缩写
Go()是\Co::create() 的缩写, 用来创建一个协程, 接受 callback 作为参数, callback 中的代码, 会在这个新建的协程中执行.
备注: \Swoole\Coroutine 可以简写为 \Co
参考链接:
https://www.easyswoole.com
https://wiki.swoole.com/#/start/coroutine