# PHP 常驻内存换上 Swoole 协程，和 Go 的 goroutine 能简单类比吗？

**URL:** <https://www.shiqidu.com/t/topic/1950>\
**Category:** 开发调优\
**Tags:** PHP, Swoole, 编程, Go, 服务器\
**Created:** [2026 年11 月 5 日 11:48 UTC](https://www.shiqidu.com/t/topic/1950 "2026-11-05T11:48:18Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![shiqidu\_editor](https://www.shiqidu.com/user_avatar/www.shiqidu.com/shiqidu_editor/32/528_2.png) [@shiqidu\_editor](https://www.shiqidu.com/u/shiqidu_editor)\
发布日期： [2026 年11 月 5 日 11:48 UTC](https://www.shiqidu.com/t/topic/1950/1 "2026-11-05T11:48:18Z")

</div>

同事甩一句「协程不就是 goroutine」之后，文档页却对不上那套直觉。从 PHP-FPM 迁到常驻进程时，最先撞上的心智模型常常是：请求别再「来一次起一次」，把阻塞 IO 换成可切换的轻量并发单位。旁边写 Go 的人听着省事，落地却容易把调度器、内存模型和生态一起糊过去。下面只把类比停在「轻量并发单位」，差异按文档分开写。

## 这篇对齐哪套文档

对照 [Open Swoole Coroutine](https://openswoole.com/docs/modules/swoole-coroutine) 页（页面上可见 OpenSwoole 与 pecl/composer 安装提示）。同日也看了 [wiki.swoole.com 中文文档入口](https://wiki.swoole.com/zh-cn/)（标明面向 Swoole4）。Open Swoole 与 Swoole 是分叉维护的两条线，命名空间、发行渠道不完全相同。下文协程行为按 **Open Swoole** 该页描述，Swoole 侧只当「同类用户态协程 + IO 切换」的对照，不把两套 API 写成可以无脑替换。

## 类比能走到哪一步

Open Swoole 文档把 coroutine 说成用户态里的「线程」：创建、销毁、切换开销低，不经过操作系统线程那一套。它不能单靠自己吃满多核，需要进程模型去铺 CPU。调度上，服务端常为每个请求建一个协程，并在 IO 等待时切换——典型是查库、打下游还没回来时，先去干别的。写法上鼓励同步风格，少写多层回调。它也提供 Channel 等在协程间同步。相对 PHP 自己的 yield/generator，文档强调 IO 切换由运行时处理，不必处处手写 yield。

Go 的 goroutine 也是「很多轻量执行体，由运行时往 OS 线程上复用」。到这一层，两边确实像：都是用户态并发单位，阻塞点附近让出执行权，开发者少碰线程池手工调度。

## 类比停住之后差在哪

运行时绑的语言不一样。Open Swoole 挂在 PHP 扩展 / 常驻服务上：请求生命周期、扩展兼容、全局变量与 static 在切换前后还信不信得过，文档都单独警告——协程共享同一地址空间，切换是隐式的，全局状态可能在你以为「顺序执行」时被别的协程改掉。Go 从语言层面就把 goroutine 当一等公民，标准库、工具链、竞态检测（race detector）围着它转，心智负担分摊方式不同。

调度器也不只是「都会切换」。Open Swoole 页写的是基于 IO 状态的协程调度，并提到 `co::run` 一类上下文：协程要跑在调度器管得到的上下文里，HTTP/Server 回调里通常已建好，自己 `go` 时要注意别嵌套错误的 `co::run`。还有 `max_coroutine` 一类上限，顶满后新协程建不出来（HTTP 场景文档举例会回到错误响应）。Go 运行时是 M:N，历史上从协作式走到可抢占，和「纯 IO 钩子让出」不是同一张图。把 Go 里「随便 go 出去」的习惯原样搬进 PHP 常驻进程，容易在连接、上下文、扩展线程安全上翻车。

生态客户端也要分开看。Open Swoole 文档列了协程版 TCP/HTTP/Redis/MySQL 等客户端，以及 Runtime Hook：把一批原生 PHP IO 函数钩到协程调度上，让同步写法跑在异步 IO 上。Go 则是标准库 `net/http`、数据库驱动等直接按 goroutine 模型设计。会写 Go 并发，不等于已经熟悉「哪些 PHP 扩展在 hook 下安全、哪些全局状态不能跨协程」。

常驻内存本身是另一刀。FPM 请求结束基本清空请求级状态。Swoole/Open Swoole 进程活着，连接池、静态缓存、单例会跨请求留下来——这是性能来源，也是泄漏和脏状态来源。goroutine 多并不自动教会你管 PHP 进程里的长寿对象。

## 怎么用这个对照

把 goroutine 当「协程大概长什么样」的入门比喻可以。据此假设调度语义、取消模型、通道用法、工具链一一对应，不行。读 Open Swoole 协程页时，优先盯：上下文怎么建、IO 何时切换、Channel / WaitGroup、hook 范围、全局变量注意点、`max_coroutine`。Go 文档用来对照「语言运行时原生并发」长什么样，而不是当 Swoole 配置手册。

你们从 FPM 迁常驻时，协程这一层是先按文档抠切换点，还是先被「跟 Go 一样」带进沟里过？
