【面试点拨】Swoole 中的协程与传统多线程对比
协程在Swoole里到底是个什么角色?一句话概括:它是在单线程内由调度器控制的用户态轻量级执行单元。共享内存、不隔离变量,需要显式传参或用Channel/WaitGroup来协调;遇到IO自动让出CPU,但碰上CPU密集任务就得老实交给task_worker;错误不会冒泡,调试时必须主动捕获。这些特
协程在Swoole里到底是个什么角色?一句话概括:它是在单线程内由调度器控制的用户态轻量级执行单元。共享内存、不隔离变量,需要显式传参或用Channel/WaitGroup来协调;遇到IO自动让出CPU,但碰上CPU密集任务就得老实交给task_worker;错误不会冒泡,调试时必须主动捕获。这些特性决定了它的使用方式和传统多线程截然不同。

协程是单线程内调度,不是多线程
Swoole 协程本质上是用户态的轻量级执行单元,跑在单个 PHP 进程的主线程里,由 Swoole 自己的调度器控制切换,完全不依赖操作系统线程调度。换句话说,你起了 1000 个 go(),底层仍然只占一个 OS 线程——除非你显式开启 enable_preemptive_scheduler 或混用了多进程。
很多开发者容易产生一个误解:“协程就是多线程”,于是代码里随手用 static 变量或全局状态,结果并发一上来数据就乱套了。原因很简单:所有协程共享同一块内存空间,却没有线程安全保护机制。
- 协程间变量不隔离:改
$GLOBALS['counter']或static $x = 0,所有协程都能读写污染 - 必须显式传参:
go(function() use ($data) { ... })才是安全边界 - 真想隔离状态?老老实实用
Swoole\Coroutine\Channel或Swoole\Coroutine\WaitGroup做协调,别指望“我以为它独立”就能万事大吉
IO 阻塞时协程自动让出,线程不会
传统多线程遇到 curl_exec() 或 mysqli_query(),会实实在在地阻塞 OS 线程,CPU 只能空转等待;而 Swoole 协程在调用被 hook 的 IO 函数(比如 Swoole\Coroutine\Http\Client->get()、Swoole\Coroutine\MySQL->query())时,会立即挂起当前协程,调度器切换给下一个就绪协程继续跑。
不过这里有个前提:你必须开启协程化支持,否则原生函数依然是同步阻塞行为。具体怎么开?
- 全局启用:
Swoole\Runtime::enableCoroutine()(推荐在Server启动前调用) - 或配置
swoole.enable_coroutine=1(php.ini) - 注意:
sleep()、usleep()默认没有协程化,得用co::sleep()
协程栈小、创建快,但无法利用多核
每个协程默认栈空间只有 2KB~8KB,创建开销微乎其微;相比之下,OS 线程通常需要 1MB+ 的栈空间,频繁创建和销毁的代价高出好几个数量级。这也是 Swoole 能轻松支撑 10w+ 并发连接的底气所在。
但代价也很明确:单个 Worker 进程内的协程无法并行利用多 CPU 核心。要想吃满多核,必须靠 Swoole 的多进程模型——也就是配置 worker_num > 1,让多个 Worker 进程各自跑一套协程调度器。
- 别指望一个
go()能加速sha256_file()这类 CPU 密集操作 - CPU 密集任务应该投递到
task_worker,避免阻塞协程调度器 task_worker_num不宜设得太高,一般是 CPU 核数的 1~2 倍就够用
协程错误不抛到外层,调试时容易丢上下文
如果在 go() 里发生了未捕获异常,或者调用了 exit()、die(),默认只会终止当前协程,不会中断主流程,也不会触发 register_shutdown_function。日志里可能只看到一行 “coroutine #123 exit”,堆栈和源头全部丢失。
实际线上踩坑最多的场景是 WebSocket 推送失败后静默退出,客户端断连了服务器完全无感知。怎么办?
- 务必在
go()内部加try/catch,并手动记录错误日志 - 检查连接有效性:
if (!$server->exist($fd)) return;,否则$server->push()可能直接抛出 warning - 慎用
Swoole\Coroutine\Channel(1):容量为 1 的 channel 在高吞吐下极易阻塞协程,建议设为1024或配合超时$ch->pop(0.1)
协程不是银弹,它的优势全建立在“你清楚自己没在干 CPU 重活、没乱共享状态、没忽略连接生命周期”的前提之上。一旦越界,表现往往比多线程更难定位——因为错误不冒泡、不 crash、只是慢或丢数据。


































