进程就是应用程序的启动实例。独立的文件资源,数据资源,内存空间。
线程属于进程,是程序的执行者。一个进程至少包含一个主线程,也可以有更多的子线程。线程有两种调度策略,一是:分时调度,二是:抢占式调度。
我的官方企鹅群
协程是轻量级线程,协程也是属于线程,协程是在线程里执行的。协程的调度是用户手动切换的,所以又叫用户空间线程。协程的创建、切换、挂起、销毁全部为内存操作,消耗是非常低的。协程的调度策略是:协作式调度。
Swoole4 由于是单线程多进程的,同一时间同一个进程只会有一个协程在运行。
Swoole rver 接收数据在 worker 进程触发 onReceive 回调,产生一个携程。Swoole 为每个请求创建对应携程。协程中也能创建子协程。
协程在底层实现上是单线程的,因此同一时间只有一个协程在工作,协程的执行是串行的。
因此多任务多协程执行时,一个协程正在运行时,其他协程会停止工作。当前协程执行阻塞 IO 操作时会挂起,底层调度器会进入事件循环。当有 IO 完成事件时,底层调度器恢复事件对应的协程的执行。。所以协程不存在 IO 耗时,非常适合高并发 IO 场景。(如下图)
协程没有 IO 等待 正常执行 PHP 代码,不会产生执行流程切换
协程遇到 IO 等待 立即将控制权切,待 IO 完成后,重新将执行流切回原来协程切出的点
协程并行协程依次执行,同上一个逻辑
协程嵌套执行流程由外向内逐层进入,直到发生 IO,然后切到外层协程,父协程不会等待子协程结束
先来看看基础的例子:
go(function () { echo "hello go1 \n";});echo "hello main \n";go(function () { echo "hello go2 \n";});
go()
是 \Co::create()
的缩写, 用来创建一个协程, 接受 callback 作为参数, callback 中的代码, 会在这个新建的协程中执行.
备注: \Swoole\Coroutine
可以简写为 \Co
上面的代码执行结果:
root@b98940b00a9b /v/w/c/p/swoole# php co.phphello go1hello mainhello go2
执行结果和我们平时写代码的顺序, 好像没啥区别. 实际执行过程:
运行此段代码, 系统启动一个新进程
遇到 go()
, 当前进程中生成一个协程, 协程中输出 heelo go1
, 协程退出
进程继续向下执行代码, 输出 hello main
再生成一个协程, 协程中输出heelo go2
, 协程退出
运行此段代码, 系统启动一个新进程. 如果不理解这句话, 你可以使用如下代码:
// co.php<?phpsleep(100);
执行并使用 ps aux
查看系统中的进程:
root@b98940b00a9b /v/w/c/p/swoole# php co.php &⏎root@b98940b00a9b /v/w/c/p/swoole# ps auxPID USER TIME COMMAND 1 root 0:00 php -a 10 root 0:00 sh 19 root 0:01 fish 749 root 0:00 php co.php 760 root 0:00 ps aux⏎
我们来稍微改一改, 体验协程的调度:
u Co;go(function () { Co::sleep(1); // 只新增了一行代码 echo "hello go1 \n";});echo "hello main \n";go(function () { echo "hello go2 \n";});
\Co::sleep()
函数功能和 sleep()
差不多, 但是它模拟的是 IO等待(IO后面会细讲). 执行的结果如下:
root@b98940b00a9b /v/w/c/p/swoole# php co.phphello mainhello go2hello go1
怎么不是顺序执行的呢? 实际执行过程:
运行此段代码, 系统启动一个新进程遇到go()
, 当前进程中生成一个协程协程中遇到 IO阻塞 (这里是 Co::sleep()
模拟出的 IO等待), 协程让出控制, 进入协程调度队列进程继续向下执行, 输出 hello main
执行下一个协程, 输出 班长管纪律小窍门hello go2
之前的协程准备就绪, 继续执行, 输出 hello go1
到这里深圳政法委书记, 已经可以看到 swoole 中 协程与进程的关系, 以及 协程的调度, 我们再改一改刚才的程序:
go(function () { Co::sleep(1); echo "hello go1 \n";});echo "hello main \n";go(function () { Co::sleep(1); echo "hello go2 \n";});
我想你已经知道输出是什么样子了:
root@b98940b00a9b /v/w/c/p/swoole# php 名人的co.phphello mainhello go1hello go2⏎
大家可能听到使用协程的最多的理由, 可能就是 协程快. 那看起来和平时写得差不多的代码, 为什么就要快一些呢? 一个常见的理由是, 可以创建很多个协程来执行任务, 所以快. 这种说法是对的, 不过还停留在表面.
首先, 一般的计算机任务分为 2 种:
CPU密集型, 比如加减乘除等科学计算IO 密集型, 比如网络请求, 文件读写等其次, 高性能相关的 2 个概念:
并行: 同一个时刻, 同一个 CPU 只能执行同一个任务, 要同时执行多个任务, 就需要有多个 CPU 才行并发: 由于 CPU 切换任务非常快, 快到人类可以感知的极限, 就会有很多任务 同时执行 的错觉了解了这些, 我们再来看协程, 协程适合的是 IO 密集型 应用, 因为协程在 IO阻塞 时会自动调度, 减少IO阻塞导致的时间损失.
我们可以对比下面三段代码:
普通版: 执行 4 个任务$n = 4;for ($i = 0; $i < $n; $i++) { sleep(1); echo microtime(true) . ": hello $i \n";};echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.php1528965075.4608: hello 01528965076.461: hello 11528965077.4613: hello 21528965078.4616: hello 3hello mainreal 0m 4.02sur 0m 0.01ssys 0m 0.00s⏎
单个协程版:$n = 4;go(function () u ($n) { for ($i = 0; $i < $n; $i++) { Co::sleep(1); echo microtime(true) . ": hello $i \n"; };});echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.phphello main1528965150.4834: hello 01528965151.4846: hello 11528965152.4859: hello 21528965153.4872: hello 3real 0m 4.03sur 0m 0.00ssys 0m 0.02s⏎
多协程版: 见证奇迹的时刻$n = 4;for ($i = 0; $i < $n; $i++) { go(function () u ($i) { Co::sleep(1); echo microtime(true) . ": hello $i \n"; });};echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.phphello main1528965245.5491: hello 01528965245.5498: hello 31528965245.5502: hello 21528965245.5506: hello 1real 0m 1.02sur 0m 0.01ssys 0m 0.00s⏎
为什么时间有这么大的差异呢:
普通写法, 会遇到 IO阻塞 导致的性能损失
单协程: 尽管 IO阻塞 引发了协程调度, 但当前只有一个协程, 调度之后还是执行当前协程
多协程: 真正发挥出了协程的优势, 遇到 IO阻塞 时发生调度, IO就绪时恢复运行
我们将多协程版稍微修改一下:
多协程版2: CPU密集型$n = 4;for ($i = 0; $i < $n; $i++) { go(function () u ($i) { // Co::sleep(1); sleep(1); echo microtime(true) . ": hello $i \n"; });};echo "hello main \n";
root@b98940b00a9b /v/w/c/p/swoole# time php co.php1528965743.4327: hello 01528965744.4331: hello 11528965745.4337: hello 21528965746.4342: hello 3hello mainr2017元旦eal 0m 4.02sur 0m 0.01ssys 0m 0.00s⏎
只是将 Co::sleep()
改成了 sleep()
, 时间又和普通版差不多了. 因为:
sleep()
可以看做是 CPU密集型任务, 不会引起协程的调度
Co::sleep()
模拟的是 IO密集型任务, 会引发协程的调度
这也是为什么, 协程适合 IO密集型 的应用.
再来一组对比的例子: 使用 redis
// 同步版, 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 () u ($cnt) { for ($i = 0; $i < $cnt; $i++) { $redis = new Co\Redis(); $redis->connect('redis', 6379); $redis->auth('123'); $redis->get('key'); }});// 多协程版, 真正使用到协程调度带来的 IO 阻塞时的调度for ($i = 0; $i < $cnt; $i++) { go(function () { $redis = new Co\Redis(); $redis->connect('redis', 6379); $redis->auth('123'); $redis->get('key'); });}
性能对比:
# 多协程版root@0124f915c976 /v/w/c/p/swoole# time php co.phpreal 0m 0.54sur 0m 0.04ssys 0m 0.23s⏎# 同步版root@0124f915c976 /v/w/c/p/swoole# time php co.phpreal 0m 1.48sur 0m 0.17ssys 0m 0.57s⏎
接触过 go 协程的 coder, 初始接触 swoole 的协程会有点 懵, 比如对比下面的代码:
package mainimport ( "fmt" "time")func main() { go func() { fmt.Println("hello go") }() fmt.Println("hello main") time.Sleep(time.Second)}
> 14:11 src $ go run test.gohello mainhello go
刚写 go 协程的 coder, 在写这个代码的时候会被告知不要忘了 time.Sleep(time.Second)
, 否则看不到输出 hello go
, 其次, hello go
与 hello main
的顺序也和 swoole 中的协程不一样.
原因就在于 swoole 和 go 中, 实做家务的收获现协程调度的模型不同.
上面 go 代码的执行过程:
运行 go 代码, 系统启动一个新进程查找package main
, 然后执行其中的 func mian()
遇到协程, 交给协程调度器执行继续向下执行, 输出 hello main
如果不添加 time.Sleep(time.Second)
, main 函数执行完, 程序结束, 进程退出, 导致调度中的协程也终止go 中的协程, 使用的 MPG 模型:
M 指的是 Machine, 一个M直接关联了一个内核线程P 指的是 processor, 代表了M所需的上下文环境, 也是处理用户级代码逻辑的处理器G 指的是 Goroutine, 其实本质上也是一种轻量级的线程而 swoole 中的协程调度使用 单进程模型, 所有协程都是在当前进程中进行调度, 单进程的好处也很明显 – 简单 / 不用加锁 / 性能也高.
无论是 go 的 MPG模型, 还是 swoole 的 单进程模型, 都是对 CSP理论 的实现.
CSP通信方式, 在1985年时的论文就已经有了, 做理论研究的人, 如果没有能提前几年, 十几年甚至几十年的大胆假设, 可能很难提高了.
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。之前说过,PHP方面的技术点很多,也是因为太多了,实在是写不过来,写过来了大家也不会看的太多,所以我这里把它整理成了PDF和文档,如果有需要的可以
点击进入暗号: PHP+「平台」
更多学习内容可以访问【对标大厂】精品PHP架构师教程目录大全,只要你能看完保证薪资上升一个台阶(持续更新)
以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的 PHP技术交流群
本文地址:https://blog.csdn.net/weixin_49163826/article/details/108870894
本文发布于:2023-04-08 23:09:41,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/cd78bcff23e5971240c8fbd9b4f8bac5.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:PHP面试遇到面试官的swoole协程三连问,快哭了!.doc
本文 PDF 下载地址:PHP面试遇到面试官的swoole协程三连问,快哭了!.pdf
留言与评论(共有 0 条评论) |