channel 必须在协程环境中创建和使用,否则首次 push/pop 时抛出“must be called in the coroutine”错误;其阻塞行为、容量语义(如 capacity=0 为同步通道)、超时控制及 close() 的唤醒机制均依赖协程调度器。

Channel 必须在协程环境里创建和使用
直接 new Swoole\Coroutine\Channel 但没在协程中调用,会报错或行为未定义。Swoole 的 Channel 不是普通对象,它依赖协程调度器管理阻塞/唤醒逻辑。
常见错误现象:Fatal error: Uncaught Swoole\Exception: must be called in the coroutine
- 必须包裹在
go()、Swoole\Coroutine\run()或已启用协程的 Server 回调(如onReceive)中 -
new Channel(1)这行本身不触发错误,但第一次push()或pop()时才会校验上下文 - 不能在 CLI 普通脚本顶层、FPM 请求主流程、或
__destruct中初始化后直接操作
push/pop 阻塞逻辑和 capacity 设置很关键
Channel 默认是阻塞模式,push 和 pop 会自动挂起当前协程,直到条件满足。这决定了你是否需要手动加超时或判断关闭状态。
参数差异:new Channel($capacity) 的 $capacity 是缓冲区大小,不是“最多存几条”,而是“空闲槽位数”。容量为 1 时,push 一次后再次 push 就会阻塞,直到有协程 pop 出一条。
- 容量设为 0 → 变成同步通道:push 必须等待另一个协程同时 pop,类似 Go 的 unbuffered chan
- 容量设为 -1 → 无限容量(不推荐):内存无限制增长,可能 OOM
-
pop(2.0)第二个参数是超时秒数,超时返回false,此时要检查$channel->errCode === SWOOLE_CHANNEL_TIMEOUT -
push()也支持超时:push($data, 1.5),超时返回false,errCode 为SWOOLE_CHANNEL_FULL
多生产者/多消费者场景下要注意 close() 时机
close() 不是“清空队列”,而是标记通道不可再写入,并唤醒所有阻塞的 pop() 协程。如果还有协程在 push,它们会收到 SWOOLE_CHANNEL_CLOSED 错误。
通过 MailChannels Email API 发送邮件,并将已签名的投递事件 Webhook 接收至 Clawdbot (Moltbot)。
容易踩的坑:一个生产者调用 close() 后,其他生产者继续 push 会失败;但消费者仍可 pop 完剩余数据,直到队列空且通道关闭。
- 不要在任意协程里随意
close(),建议由统一协调者(比如主协程或 WaitGroup 完成后)执行 - 消费者循环里必须同时检查
$data === false和$channel->errCode,仅靠if (!$data)无法区分超时和关闭 - 关闭后调用
push()返回false,errCode 是SWOOLE_CHANNEL_CLOSED;调用pop()若队列非空仍能取到数据,空了才返回false并设 errCode
Channel 不能跨进程,别和连接池逻辑混淆
同一个 Channel 实例只在当前进程的协程间有效。Worker 进程之间内存隔离,Channel 对象无法传递或共享。
典型误用:在 onWorkerStart 创建一个全局 Channel,想让所有 Worker 内的协程都往里 push —— 这完全无效,每个 Worker 进程都有自己的副本。
- 进程间通信请用
Swoole\Table、Redis、UnixSocket或TaskWorker + task() - 连接池内部用
Channel管理空闲连接,是因为所有连接获取/归还操作都在同一 Worker 进程的协程中完成 - 如果你看到“Channel 实现连接池”,那一定是在单个 Worker 进程内闭环使用的,不是跨 Worker 的“全局池”
最易被忽略的一点:Channel 的零拷贝只对 PHP 引用计数生效,如果你在 push 前做了 json_encode 或 serialize,大字符串依然会复制。传数组、对象时,确保它们没被意外 clone 或强制转换。










