协程是单线程内由swoole调度器控制的用户态轻量级执行单元,共享内存且不隔离变量,需显式传参或用channel/waitgroup协调;io自动让出但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 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 全局启用:
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)










