swoole 协程介绍
协程的执行顺序:
| 1 2 3 4 5 6 7 8 9 | go(function() {    echo"hello go1 \n";});echo"hello main \n";go(function() {    echo"hello go2 \n";}); | 
go() 是 \Co::create() 的缩写,用来创建一个协程,接受callback作为参数,callback中的代码。会在这个新建的协程中执行。
备注:\Swoole\Coroutine 可以简写为 \Co
上面的代码执行结果:
| 1 2 3 4 | # php co.phphello go1hello mainhello go2 | 
实际执行过程:
- 运行此段代码,系统启动一个新进程
- 遇到 go() ,当前进程中生成一个协程,协程中输出 hello go1,协程退出
- 进程继续向下执行代码,输出 hello main
- 再生成一个协程,协程中输出 hello go2,协程退出
下面稍微改一下执行顺序
| 1 2 3 4 5 6 7 8 9 10 11 12 | useCo;go(function() {    Co::sleep(1); // 只新增了一行代码    echo"hello go1 \n";});echo"hello main \n";go(function() {    echo"hello go2 \n";}); | 
\Co::sleep() 函数功能和 sleep() 差不多,但是它模拟的是IO等待,执行的顺序如下:
| 1 2 3 4 | # php co.phphello mainhello go2hello go1 | 
上面的实际执行过程如下:
- 运行此段代码,系统启动一个进程
- 遇到 go(),当前进程中生成一个协程
- 协程中遇到IO阻塞(这里是 Co::sleep() 模拟出来的IO等待),协程让出控制,进入协程调度队列
- 进程继续向下执行,输出 hello main
- 执行下一个协程,输出 hello go2
- 之前的协程准备就绪,继续执行,输出 hello go1
协程快在哪?减少IO阻塞导致的性能损失
一般的计算机任务分为两种:
- CPU密集型,比如加减乘除等科学计算
- IO密集型,比如网络请求,文件读写等
高性能相关的两个概念:
- 并行:同一个时刻,同一个CPU只能执行同一个任务,要同时执行多个任务,就需要有多个CPU才行
- 并发:由于CPU切换任务非常快,所以让人感觉像是有多个任务同时执行
协程适合的场景是IO密集型应用,因为协程在IO阻塞时会自动调度,减少IO阻塞导致的时间损失。
普通版:执行4个任务
| 1 2 3 4 5 6 | $n= 4;for($i= 0; $i< $n; $i++) {    sleep(1);    echomicrotime(true) . ": hello $i \n";};echo"hello main \n"; | 
执行结果:
| 1 2 3 4 5 6 7 8 9 | # php co.php1528965075.4608: hello 01528965076.461: hello 11528965077.4613: hello 21528965078.4616: hello 3hello mainreal    0m 4.02suser    0m 0.01ssys     0m 0.00s | 
单个协程版:
| 1 2 3 4 5 6 7 8 | $n= 4;go(function() use($n) {    for($i= 0; $i< $n; $i++) {        Co::sleep(1);        echomicrotime(true) . ": hello $i \n";    };});echo"hello main \n"; | 
执行结果:
| 1 2 3 4 5 6 7 8 9 | # php co.phphello main1528965150.4834: hello 01528965151.4846: hello 11528965152.4859: hello 21528965153.4872: hello 3real    0m 4.03suser    0m 0.00ssys     0m 0.02s | 
多协程版本:
| 1 2 3 4 5 6 7 8 | $n= 4;for($i= 0; $i< $n; $i++) {    go(function() use($i) {        Co::sleep(1);        echomicrotime(true) . ": hello $i \n";    });};echo"hello main \n"; | 
执行结果:
| 1 2 3 4 5 6 7 8 9 | # php co.phphello main1528965245.5491: hello 01528965245.5498: hello 31528965245.5502: hello 21528965245.5506: hello 1real    0m 1.02suser    0m 0.01ssys     0m 0.00s | 
这三种版本为什么时间上有很大的差异?
- 普通版本:会遇到IO阻塞,导致的性能损失
- 单协程版本:尽管IO阻塞引发了协程调度,但当前只有一个协程,调度之后还是执行当前协程
- 多协程版本:真正发挥出协程的优势,遇到IO阻塞时发生调度,IO就绪时恢复运行
下面将多协程版本修改为CPU密集型
| 1 2 3 4 5 6 7 8 9 | $n= 4;for($i= 0; $i< $n; $i++) {    go(function() use($i) {        // Co::sleep(1);        sleep(1);        echomicrotime(true) . ": hello $i \n";    });};echo"hello main \n"; | 
执行的结果:
| 1 2 3 4 5 6 7 8 9 | # php co.php1528965743.4327: hello 01528965744.4331: hello 11528965745.4337: hello 21528965746.4342: hello 3hello mainreal    0m 4.02suser    0m 0.01ssys     0m 0.00s | 
只是将 Co::sleep() 改成了sleep() ,时间又和普通版本差不多,原因是:
- sleep() 可以看做是CPU密集型任务,不会引起协程的调度
- Co::sleep() 模拟的是IO密集型任务,会引发协程的调度
这就是为什么协程适合IO密集型应用。
下面使用一组对比,使用redis:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // 同步版, redis使用时会有 IO 阻塞$cnt= 2000;for($i= 0; $i< $cnt; $i++) {    $redis= new\Redis();    $redis->connect('redis');    $redis->auth('123');    $key= $redis->get('key');}// 单协程版: 只有一个协程, 并没有使用到协程调度减少 IO 阻塞go(function() use($cnt) {    for($i= 0; $i< $cnt; $i++) {        $redis= newCo\Redis();        $redis->connect('redis', 6379);        $redis->auth('123');        $redis->get('key');    }});// 多协程版, 真正使用到协程调度带来的 IO 阻塞时的调度for($i= 0; $i< $cnt; $i++) {    go(function() {        $redis= newCo\Redis();        $redis->connect('redis', 6379);        $redis->auth('123');        $redis->get('key');    });} | 
性能对比:
| 1 2 3 4 5 6 7 8 9 10 11 | # 多协程版# php co.phpreal    0m 0.54suser    0m 0.04ssys     0m 0.23s# 同步版# php co.phpreal    0m 1.48suser    0m 0.17ssys     0m 0.57s | 
swoole协程和go协程对比:单进程 VS 多线程
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | packagemainimport(    "fmt"    "time")funcmain() {    gofunc() {        fmt.Println("hello go")    }()    fmt.Println("hello main")    time.Sleep(time.Second)} | 
执行结果:
| 1 2 3 | $ gorun test.gohello mainhello go | 
go代码的执行过程如下:
- 运行 go 代码,系统启动一个新进程
- 查找 package main ,然后执行其中的 func main()
- 遇到协程,交给协程调度器执行
- 继续向下执行,输出 hello main
- 如果不添加 time.Sleep(time.Second),main函数执行完,程序结束,进程退出,导致调度中的协程也终止
swoole和go实现协程调度的模型不同,go中使用的是MPG模型:
- M 指的是 Machine, 一个M直接关联了一个内核线程
- P 指的是 processor, 代表了M所需的上下文环境, 也是处理用户级代码逻辑的处理器
- G 指的是 Goroutine, 其实本质上也是一种轻量级的线程

而swoole中的协程调度使用单进程模型,所有协程都是在当前进程中进行调度,单进程的好处是:简单 / 不用加锁 / 性能高。
swoole 协程介绍的更多相关文章
- [转]Unity3D协程介绍 以及 使用
		作者ChevyRay ,2013年9月28日,snaker7译 原文地址:http://unitypatterns.com/introduction-to-coroutines/ 在Unity中,协 ... 
- [Sw] Swoole-4.2.9 可以尝试愉快应用 Swoole 协程
		大家知道 Swoole 提供了方便于服务器.网络编程的模式,简化了多进程编程. 这直接让 PHP 的运行很容易变成常驻内存的 Server 程序,执行效率上有了数倍的提升. 但是这一切还没有让人足够兴 ... 
- python 全栈开发,Day43(引子,协程介绍,Greenlet模块,Gevent模块,Gevent之同步与异步)
		昨日内容回顾 I/O模型,面试会问到I/O操作,不占用CPU.它内部有一个专门的处理I/O模块.print和写log 属于I/O操作,它不占用CPU 线程GIL保证一个进程中的多个线程在同一时刻只有一 ... 
- {python之协程}一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二
		python之协程 阅读目录 一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二 一 引子 本 ... 
- Unity3D协程介绍 以及 使用
		作者ChevyRay ,2013年9月28日,snaker7译 原文地址:http://unitypatterns.com/introduction-to-coroutines/ 在Unity中,协 ... 
- 【python】-- 协程介绍及基本示例、协程遇到IO操作自动切换、协程(gevent)并发爬网页
		协程介绍及基本示例 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是协程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他 ... 
- Swoole 协程 MySQL 客户端与异步回调 MySQL 客户端的对比
		Swoole 协程 MySql 客户端与 异步回调 MySql 客户端的对比 为什么要对比这两种不同模式的客户端? 异步 MySQL 回调客户端是虽然在 Swoole 1.8.6 版本就已经发布了, ... 
- Swoole 协程与 Go 协程的区别
		Swoole 协程与 Go 协程的区别 进程.线程.协程的概念 进程是什么? 进程就是应用程序的启动实例. 例如:打开一个软件,就是开启了一个进程. 进程拥有代码和打开的文件资源,数据资源,独立的内存 ... 
- Swoole协程与传统fpm同步模式比较
		如果说数组是 PHP 的精髓,数组玩得不6的,根本不能算是会用PHP.那协程对于 Swoole 也是同理,不理解协程去用 Swoole,那就是在瞎用. 首先,Swoole 只能运行在命令行(Cli)模 ... 
随机推荐
- ansible使用,常用模块
			使用ansible管理其他主机有两种方式: 1.命令行执行ansible ad-hoc命令 2.把要做的动作行为写入一个文件[playbook脚本],ansible读取脚本自动完成相应的任务. Ans ... 
- 如何用canvas拍出 jDer's工作照
			背景 在京东,就职满五年的老员工被称作"大佬",如果满了十年,那就要被称之为"超级大佬"了. 从 2016 年 5 月 19 日开始,每一年的这一天都被定为京东 ... 
- ParticleSystem的小优化
			最近游戏场景希望加入一些ParticleSystem来丰富场景,这样肯定会带来一定的性能开销.一般来说ParticleSystem的优化就是尽量减少粒子数,尽量不使用粒子碰撞等.而今天要说的是,多个P ... 
- burpsuite抓包乱码问题
			网上百度说只需要Change Font选择中文字体即可,但是我这边试过还是乱码,按照网上一篇博客说抓包中按钮展示乱码的问题,在下面Character Sets选择Use a specific char ... 
- jmeter的用途
			1.可以测接口 2.测试连数据库 3.可以进行压测 4.可部署分布式 
- sql中的join
			首先准备数据 有以下数据,三张表:role(角色表).hero(英雄表).skill(技能表),我们以英雄联盟的数据做示例 一个hero对应一个role(我们这里暂定) 一个role可以对应多个her ... 
- rocketmq-console修改logo,修改ip,修改port及完整编译安装图文版
			一.下载源码到本地 这里使用IDEA,作为编译工具 https://gitee.com/mrliuNumberOne/rocketmq-externals.git 导入成功后如图: 二.Maven编译 ... 
- 谈谈 Java 中的那些“琐”事
			一.公平锁&非公平锁 是什么 公平锁:线程按照申请锁的顺序来获取锁:在并发环境中,每个线程都会被加到等待队列中,按照 FIFO 的顺序获取锁. 非公平锁:线程不按照申请锁的顺序来获取锁:一上来 ... 
- 面试官:分库分表之后,id 主键如何处理?
			面试题 分库分表之后,id 主键如何处理? 面试官心理分析 其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那肯定不对啊,需要一个全 ... 
- spring aop原理和实现
			一.aop是什么 1.AOP面向方面编程基于IoC,是对OOP的有益补充: 2.AOP利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了 多个类的公共行为封装到一个可 ... 
