协程间通信必须用Swoole\Coroutine\Channel,因pcntl_pipe等创建的是阻塞I/O句柄,协程调度器无法接管;信号处理须在主进程注册,回调内禁用协程API,推荐通过Channel解耦信号与业务逻辑。

协程间不能直接用 PHP 原生 pipe 或 stream_socket_pair 通信,必须用 Swoole\Coroutine\Channel;信号处理必须在主进程注册,且回调内禁止调用协程 API。
协程间通信为什么不能用 pcntl_pipe 或 stream_socket_pair
这些函数创建的是操作系统级的阻塞 I/O 句柄,Swoole 协程调度器无法接管其读写行为。一旦在协程中 fread 或 stream_select,整个 Worker 进程会卡死——因为底层没被 SWOOLE_HOOK_ALL hook,协程无法挂起让出控制权。
实际错误现象包括:
- 协程看似“卡住”,但
co::stats()显示协程数不增不减 - 其他并发请求完全无响应,
strace -p $pid可见进程停在read()系统调用上 - 启用
SWOOLE_HOOK_ALL后仍无效,说明该函数根本不在 Swoole 的 hook 列表里
正确做法只有一条:Swoole\Coroutine\Channel 是纯内存队列,无系统调用开销,push/pop 全部非阻塞(容量满或空时才挂起协程)。
Swoole\Process::signal 必须在主进程调用
Worker 进程默认忽略大部分信号,SIGTERM、SIGUSR1 等只有 Master 进程能可靠捕获。若在 onWorkerStart 里注册信号处理器,大概率失效——因为子进程继承了父进程的信号掩码,且 Swoole 内部已屏蔽了部分信号传递。
典型误用场景:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 在
onRequest回调里调用Swoole\Process::signal—— 每次请求都注册一次,泄漏回调函数 - 在 TaskWorker 中注册
SIGINT—— 该进程无权响应终端中断,信号被丢弃 - 信号回调里调用
go()或$server->shutdown()—— 这些是协程 API,而信号回调运行在同步上下文
安全做法:仅在 onStart 或 onManagerStart 中注册,回调内只做标记、发消息、调用 posix_kill 转发信号给 Worker。
Channel 与信号组合使用的常见模式
真正可靠的解耦方式,是把信号事件转为 Channel 消息,交由主协程统一处理。这样既规避了信号回调的限制,又保持了业务逻辑的可测试性。
关键点:
- 全局声明一个
Channel实例(如$signalChan = new Swoole\Coroutine\Channel(1)),在onStart注册信号时向它push信号值 - 在
Co\run()主循环里pop该 Channel,根据信号类型执行 shutdown、reload 或日志轮转 - 务必设置 Channel 容量为 1,避免多次信号挤压导致丢失(
SIGTERM只需响应一次) - 不要在信号回调里
closeChannel —— 关闭操作应由主协程完成,否则可能引发Channel is closed异常
这种模式下,co::sleep、$server->shutdown() 等协程安全操作全部可控,不会污染信号处理路径。
最易被忽略的是 Channel 容量和信号重复触发的竞态——哪怕只注册一次 SIGTERM,内核也可能因网络延迟或进程状态抖动发送多次,没做限流的 Channel 会直接崩掉后续逻辑。

















