task进程是独立子进程,非协程,同步阻塞执行;需手动启用协程但不推荐;task()失败主因是未配task_worker_num、早期投递或数据超限;ontask与onfinish跨进程执行,状态不可共享;task崩溃不自动重启,须用task_max_request兜底。

Task进程不是协程,是独立子进程
很多人误以为TaskWorker能跑协程,其实它和Worker进程一样,是Manager fork出来的**独立子进程**,每个Task进程内是同步阻塞执行的,不带协程调度器。你在onTask回调里写sleep(1),只会卡住当前Task进程,不影响其他Task或Worker——但会拖慢任务队列响应。
常见错误现象:onTask里直接用PDO或file_get_contents()导致整个Task进程阻塞数秒;或者试图在onTask里调用go(),结果报错Coroutine is not running。
- Task进程启动后不自动启用协程,如需协程能力,必须手动调用
Swoole\Runtime::enableCoroutine() - 但一般不建议:Task本就为处理“重、慢、稳”的任务,加协程反而增加调度开销,且易引发资源竞争
- 真正需要异步IO的任务(比如发HTTP请求),应改用Worker中协程客户端 + Channel通知,而非扔进Task
task()投递失败的三种典型原因
$server->task()看似简单,但返回false时往往没报错,排查靠日志和状态检查。最常踩的坑不是代码写错,而是配置或生命周期问题。
- 未设置
task_worker_num > 0:哪怕只设1,漏配就直接静默失败 - 在
onStart或onWorkerStart早期阶段投递:此时Task进程可能尚未就绪,$server->taskworker为false,投递被丢弃 - 投递数据超限:默认8KB以内走管道直传;超过则写临时文件,若磁盘满、权限不足或
tmpdir不可写,task()返回false且无异常抛出
验证方式:在onWorkerStart里加var_dump($server->taskworker);,确保只在Worker进程中投递;投递后立刻检查返回值,false时记录error_log("task failed: " . swoole_last_error());。
onTask和onFinish的执行不在同一进程
onTask在Task进程执行,onFinish在发起投递的那个Worker进程执行——这是进程隔离的硬约束。你不能在onTask里修改Worker里的某个static $cache,也不能依赖onFinish的执行顺序与onTask一一对应。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
使用场景中容易忽略这点:比如想用Task预热Redis连接池,然后在onFinish里把连接句柄塞进Worker的Table里供后续请求复用。这行不通——句柄无法跨进程传递,且Task进程退出后连接即销毁。
- 进程间只能传序列化数据(数组、字符串、数字),不能传资源、闭包、对象实例
- 需要共享状态,必须走外部存储:
Swoole\Table、Redis、MySQL,而不是PHP变量 -
onFinish的$task_id和$data来自onTask的return或$server->finish(),但$task_id不保证单调递增,也不保证按投递顺序完成
Task进程崩溃不会自动重启,得靠max_request兜底
Task进程一旦因段错误、内存溢出或未捕获异常崩溃,Manager进程默认**不会重启它**,task_worker_num数量会永久减少。线上服务跑几天后Task数从16掉到12,吞吐骤降,却查不到Crash日志——这就是典型表现。
根本原因:Swoole的TaskWorker没有像Worker那样内置max_request机制,也没有自动守护重启逻辑。
- 必须主动在
onTask里包try/catch,并在catch中error_log()和return,避免未捕获异常终止进程 - 对计算密集型任务,加超时控制:
declare(ticks=1); $start = microtime(true); while (...) { if (microtime(true) - $start > 10) break; } - 终极兜底:设置
task_max_request(Swoole 4.8+支持),让Task进程处理固定请求数后主动退出,由Manager拉起新进程
这个参数在文档里藏得深,很多团队线上跑了两年才发现Task进程越跑越少——它不像worker_max_request那么显眼,但同样关键。










