co::resume是手动唤醒协程,不参与调度循环,仅将协程置为就绪;channel->push则通过通信自动触发pop协程恢复,由调度器透明协调,更安全可靠。

co::resume 是手动恢复协程,不是调度器自动行为
co::resume 用于显式唤醒一个已挂起(co::yield 或 I/O 阻塞)的协程,但它**不参与 Swoole 的协程调度循环**。它只是把目标协程从“等待恢复”状态拉回“就绪”队列,最终是否立即执行,取决于当前调度器是否空闲、有没有更高优先级协程在跑。常见误用是以为调了 co::resume 就等于“立刻切过去执行”,实际可能延迟几个毫秒甚至更久。
典型场景是配合 co::create + co::yield 手动实现简单协作逻辑,比如状态机驱动的任务切换:
go(function () {
echo "A\n";
co::yield(); // 暂停自己
echo "C\n";
});
go(function () {
echo "B\n";
co::resume($cid); // $cid 是上一个协程 ID,但需提前保存
echo "D\n";
});
但注意:co::resume 要求传入合法协程 ID,且该协程必须处于 YIELD 状态;若协程已结束或正在运行,调用会静默失败或抛出警告。
channel->push 是生产消息,触发 pop 协程自动 resume
Channel->push 本身不直接恢复任何协程,它的作用是往通道里写入数据;但一旦有另一个协程正在调用 Channel->pop 并阻塞等待,Swoole 底层会**自动 resume 那个 pop 协程**——这个过程是调度器内置的、透明的,你不需要也**不能**手动干预。
这是 CSP(Communicating Sequential Processes)模型的核心机制:协程通过通道通信,通信即同步信号。常见陷阱包括:
-
Channel->push在无协程等待时会立即返回true(非阻塞模式)或挂起当前协程(阻塞模式),取决于构造时的$capacity参数 - 如果
Channel容量为 0(默认),push必定阻塞,直到有协程pop;此时当前协程让出控制权,调度器转去跑别的协程 - 多个协程同时
pop,push只唤醒其中一个,顺序由调度器决定,不可预测
resume 和 push 的根本差异在于控制流所有权
co::resume 是“我主动叫醒你”,属于协程间硬控制,容易破坏调度公平性,也难追踪依赖关系;而 Channel->push 是“我发消息,谁要谁拿”,属于声明式通信,调度完全由 Swoole 自动协调。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
实际开发中应优先用 Channel,除非你明确需要极细粒度的手动协同(比如实现自定义锁、状态同步器)。滥用 co::resume 容易导致:
- 协程 ID 泄露或失效(协程退出后 ID 不再有效)
- 竞态:两个地方同时
resume同一个协程,第二次无效但无提示 - 死锁:A resume B,B 又 resume A,中间没 yield,形成无限循环占用 CPU
真正需要手动 resume 的场景极少,绝大多数“唤醒”需求,用 Channel、WaitGroup 或 defer 更安全、更符合 Swoole 协程设计本意。
协程上下文丢失是 resume 最隐蔽的坑
当你在某个协程内调用 co::resume($cid),被恢复的协程会继续执行,但它**不会继承调用者的上下文**:局部变量、try/catch 堆栈、资源句柄全都不共享。这意味着你不能靠 resume 来“传递错误”或“延续事务”,它只是跳转指令,不是函数调用。
而 Channel->push 发送的是值(可序列化对象、数组等),接收方协程拿到的是副本,天然隔离。这也是为什么连接池、任务分发这类场景必须用 Channel,而不是靠 resume 串起来。
一句话收尾:resume 是底层工具,push 是编程范式;别为了省一行代码,绕过 Swoole 花大力气封装好的协作模型。










