协程中单例“唯一性”仅限当前协程:静态变量不隔离导致并发创建、资源冲突、数据污染;应改用协程上下文、显式传参或连接池,禁用序列化并重置状态。
PHP 的 Singleton 类在传统 FPM 下能保证整个请求生命周期内只有一个实例,但在 Swoole 协程中,这个“唯一”被大幅削弱了——self::$instance 是静态变量,属于进程级,但协程调度是用户态的,多个协程并发执行同一段代码时,if (self::$instance === null) 可能被同时判定为 true,导致多次 new。
常见错误现象:getInstance() 返回不同对象(var_dump($a === $b) 为 false),数据库连接复用失败、配置被覆盖、日志写入错乱。
SwooleCoroutineMySQL 连接),多个协程共用一个连接会引发并发读写冲突真正适配协程的“全局唯一访问点”,应基于 SwooleCoroutine::getContext() 或显式传参,而非静态属性。
实操建议:
go(function() use ($db) { ... }) 显式传递已初始化的连接对象,避免任何全局静态引用SwooleCoroutine::getContext() 存储一次,后续在同协程内通过 getContext() 获取,协程间天然隔离SwooleCoroutineChannel + 单独协程托管,由它统一创建/分发连接,其他协程只取不造在协程高频复用场景下,对象可能被 serialize/unserialize 传输(如跨协程投递任务),此时仅靠 __wakeup 抛异常并不安全——异常可能被吞,或在非预期时机触发。
更稳妥的做法:
public function __sleep() { throw new Exception('Singleton cannot be serialized'); }
json_encode + 重建逻辑,不直接反序列化对象__destruct 是否释放了协程独占资源(如未关闭的 SwooleCoroutineHttpClient),否则可能造成连接泄漏Swoole Server 是常驻内存的,static 属性不会随请求结束而销毁。如果单例里缓存了用户数据(如 $_SESSION 映射)、临时 token 或未清理的 query 结果,下一个协程进来时可能直接读到上一个用户的脏数据。
关键动作:
resetForRequest()),并在每次 HTTP 请求入口手动调用Co::getContext(),而不是依赖静态属性承载请求级状态