swoole\event::wait()阻塞等待所有异步任务完成,event::dispatch()仅单次轮询不阻塞;协程程序中应避免直接使用二者,而由coroutine\scheduler自动管理。

Event::wait 会阻塞进程直到所有事件处理完毕
Swoole\Event::wait() 的作用是进入事件循环并挂起当前进程,持续监听 I/O、定时器、信号等事件,直到没有任何待处理的异步任务(包括未完成的回调、未触发的定时器、未关闭的连接等)才退出。它不返回值,只做一件事:等完再走。
常见错误现象是脚本一闪而过、没输出、回调没执行——基本就是忘了加这句,尤其在 CLI 脚本中用 Swoole\Async\Redis 或 Swoole\Async\HttpClient 时。
- 必须放在所有异步操作注册之后、脚本结束之前
- 不能重复调用;第二次调用会直接返回(因为事件循环已退出)
- 在 Swoole 5 中,不再自动补上,必须手动写,否则脚本立刻退出
- 和
Co::sleep(0)或Coroutine\Scheduler不兼容,混用会导致调度混乱或死锁
Event::dispatch 是单次轮询,不阻塞也不等待
Swoole\Event::dispatch() 只执行一次事件循环迭代:检查当前就绪的事件、触发对应回调、然后立即返回。它不会等待新事件到来,也不会阻塞,适合嵌入到已有主循环中(比如游戏帧循环、自定义调度器),或需要精细控制事件处理节奏的场景。
典型误用是把它当 wait() 用,比如:
go(function () {
Co::sleep(1);
echo "done\n";
});
Swoole\Event::dispatch(); // ❌ 这里几乎立刻返回,协程根本没机会跑
它适合的场景包括:
- 与
pcntl_signal_dispatch()配合,在信号处理后主动推进一次事件循环 - 在 GUI 应用或嵌入式 PHP 环境中,避免独占主线程
- 调试时逐轮查看事件触发顺序(配合
strace或日志) - 某些旧版 Workerman 自定义 EventLoop 的适配层中作为底层驱动调用
协程环境下它们都不该直接用
在 go()、Co::create() 或 Coroutine\Scheduler 主导的协程程序中,Event::wait() 和 Event::dispatch() 都属于「低层事件循环接口」,由 Swoole 内部协程调度器自动管理。手动调用容易破坏调度状态。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
比如:
- 在
go()启动协程后调用Event::wait(),可能让主线程卡住,而协程其实已在后台运行(但无法被正确调度) - 在
Coroutine\Scheduler的start()之后再调用Event::dispatch(),会干扰其内置的epoll_wait循环 - 协程 I/O(如
Co\Http\Client->get())不依赖Event类,而是走 fiber + hook 机制,跟这两个函数无直接关系
真正该用的是 Coroutine\Scheduler 或确保脚本末尾有 Event::wait()(仅限纯异步、非协程脚本)。
底层实现差异影响实际行为
Event::wait() 最终调用的是 C 层的 swoole_event_wait(),内部是一个 while 循环 + epoll_wait(),直到 event_num == 0 才 break。
Event::dispatch() 对应的是 swoole_event_dispatch(),只调一次 epoll_wait(..., timeout=0),即「只看此刻有没有就绪事件」,没有就直接返回。
这意味着:
- 网络抖动或延迟高时,
dispatch()可能连续多次返回却没触发任何回调 -
wait()在空闲时 CPU 占用为 0(epoll 休眠),而反复调dispatch()若没加 sleep,会变成 busy-loop - 超时控制方式不同:
wait()没参数,dispatch()也无法传超时,真要限时得自己套microtime()+ 计数
真正需要精确控制事件循环节奏的,应该用 Swow 或 Revolt 这类现代 Fiber 事件循环,而不是在 Swoole 的 Event 上硬拧。










