参考文献
新版文档:https://wiki.swoole.com/#/runtime
新版文档(协程高级):https://wiki.swoole.com/#/coroutine
新版文档(协程调度):https://wiki.swoole.com/#/coroutine/scheduler?id=coroutinescheduler
新版文档(协程api):https://wiki.swoole.com/#/coroutine/coroutine?id=create
旧版文档:https://wiki.swoole.com/wiki/page/p-coroutine.html
PHP yield 关键字:https://www.php.net/manual/zh/language.generators.syntax.php
代码下载:https://github.com/mailjobblog/dev_swoole/tree/master/210525_coroutine
swoole协程运行方式
协程是轻量级线程,协程也是属于线程,协程是在线程里执行的。协程的调度是用户手动切换的,所以又叫用户空间线程。协程的创建、切换、挂起、销毁全部为内存操作,消耗是非常低的。协程的调度策略是:协作式调度。
- Swoole4 由于是单线程多进程的,同一时间同一个进程只会有一个协程在运行。
- Swoole server 接收数据在 worker 进程触发 onReceive 回调,产生一个协程。Swoole 为每个请求创建对应携程。协程中也能创建子协程。
- 协程在底层实现上是单线程的,因此同一时间只有一个协程在工作,协程的执行是串行的。
- 因此多任务多协程执行时,一个协程正在运行时,其他协程会停止工作。当前协程执行阻塞 IO 操作时会挂起,底层调度器会进入事件循环。当有 IO 完成事件时,底层调度器恢复事件对应的协程的执行。。所以协程不存在 IO 耗时,非常适合高并发 IO 场景。(如下图)
- 协程没有 IO 等待 正常执行 PHP 代码,不会产生执行流程切换
- 协程遇到 IO 等待 立即将控制权切,待 IO 完成后,重新将执行流切回原来协程切出的点
- 协程并行协程依次执行,同上一个逻辑
- 协程嵌套执行流程由外向内逐层进入,直到发生 IO,然后切到外层协程,父协程不会等待子协程结束
多进程模型和协程模型应用场景
首先, 一般的计算机任务分为 2 种:
- CPU密集型, 比如加减乘除等科学计算
- IO 密集型, 比如网络请求, 文件读写等
其次, 高性能相关的 2 个概念:
- 并行: 同一个时刻, 同一个 CPU 只能执行同一个任务, 要同时执行多个任务, 就需要有多个 CPU 才行
- 并发: 由于 CPU 切换任务非常快, 快到人类可以感知的极限, 就会有很多任务 同时执行 的错觉
了解了这些, 我们再来看协程, 协程适合的是 IO 密集型
应用, 因为协程
在 IO阻塞 时会自动调度
,减少IO阻塞
导致的时间损失
测试注意事项
sleep()
可以看做是CPU密集型
任务, 不会引起协程的调度Co::sleep()
模拟的是IO密集型
任务, 会引发协程的调度
协程实现方式
Scheduler
<?php
$scheduler = new Swoole\Coroutine\Scheduler;
$scheduler->add(function(){
Swoole\Coroutine::sleep(3);
echo 'i am Coroutine'.PHP_EOL;
});
$scheduler->add(function(){
Swoole\Coroutine::sleep(1);
echo 'i am Coroutine2222222222222222'.PHP_EOL;
});
$scheduler->start();
Coroutine
<?php
echo 'start'.PHP_EOL;
Swoole\Coroutine::create(function(){
Swoole\Coroutine::sleep(3);
echo 'i am Coroutine'.PHP_EOL;
});
echo 'end'.PHP_EOL;
go
<?php
echo 'start'.PHP_EOL;
go(function(){
co::sleep(3);
echo 'i am Coroutine'.PHP_EOL;
});
echo 'end'.PHP_EOL;
Go\run
<?php
echo 'start'.PHP_EOL;
Go\run(function(){
co::sleep(3);
echo 'i am Coroutine'.PHP_EOL;
});
echo 'end'.PHP_EOL;