你以为 GitHub Actions 只能串行跑步骤?今天它把这件事用四个关键字彻底原生化了
很多团队在 GitHub Actions 里想跑「并行构建」时,干的第一件事是把同一个 job 拆成多个 job——Linux 一个、macOS 一个、Windows 一个,然后用 matrix 或者 needs 拼起来。跑是跑起来了,但每个 job 都要重新 checkout、重新装依赖、重新传 artifact。机器多了,时间省了,资源反而多花了。
而另一种更常见的场景,根本没人想着拆 job:同一个 Linux 环境里,lint、typecheck、单元测试其实互不依赖,但只能一个跑完再跑下一个,10 分钟里可能有 6 分钟在等。
2026 年 6 月 25 日,GitHub Actions 终于把这件事原生化了。
这次解决了什么问题
在这次更新之前,同一个 job 里的 steps 永远是串行的。有人试过用 shell 后台运行符 & 把任务丢到后台,然后在下一个 step 里 wait $! 等它跑完。这在技术上可行,但有个致命问题:所有后台任务的输出会混在一个 log 流里,一旦失败根本没法分开看是谁的错。
GitHub 官方自己在公告里也承认了这一点——他们管这叫「logs get interleaved」,然后说现在 four new keywords 把这个问题彻底修了。
四个关键字分别是:
background: true ——把一个 step 异步启动,立即跳到下一个 step,不用等它跑完。
wait / wait-all ——暂停执行,等一个或多个指定的后台 step 完成。wait 等待特定名字的 step,wait-all 等待目前为止所有已启动的后台 step。
cancel ——当不再需要某个后台 step 时,体面地把它停掉。最典型的用法是:启动一个测试数据库服务,跑完测试,然后把它关掉。
parallel ——把一组 steps 一次性全部后台启动,全部跑完之后再继续往下走。是「把这几个 step 并行跑,然后等它们全部完成」的语法糖。
实际怎么用
先看一个最常见的:同一个 job 里并行跑 lint、typecheck、unit tests,三个任务完全独立,共享同一个 checkout 和 node_modules。
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run linting
run: npm run lint
background: true
- name: Run type check
run: npx tsc --noEmit
background: true
- name: Run unit tests
run: npm test
background: true
- name: Wait for all checks
run: echo "All checks finished"
wait-all: true
- name: Build
run: npm run build
三个检查并行跑,总耗时变成最慢的那一个,而不是三个加起来。logs 各自独立,哪个失败一目了然。
再看 parallel 关键字的等效写法,上面这组也可以写成:
- name: Run all checks
parallel:
- name: Lint
run: npm run lint
- name: Type check
run: npx tsc --noEmit
- name: Unit tests
run: npm test
再比如,需要先启动一个数据库服务再跑测试:
- name: Start PostgreSQL
id: db
run: docker compose up -d db
background: true
- name: Wait for DB ready
run: until pg_isready; do sleep 1; done
wait: Start PostgreSQL
- name: Run migrations
run: npx prisma migrate deploy
- name: Run tests
run: npm test
- name: Stop PostgreSQL
run: docker compose down
cancel: Start PostgreSQL
在 cancel 之前,数据库一直在后台跑。测试跑完,不管成功还是失败,cleanup step 都会把服务停掉。
一个容易踩的坑
background: true 只代表「这个 step 启动了」,不代表它依赖的服务已经就绪。所以 health check 或 readiness 脚本仍然必要,上面的 wait for DB ready 就是这个原因。
另一个实际的限制:一个 job 同时最多跑 10 个后台 step。超过 10 个需要分到多个 job 里去。
还有一点:background step 里设置的 env vars 或 outputs,在 wait 之前读取是不可靠的,因为 step 可能还没跑完。这点和普通 step 不同,需要注意。
和 job 级并行的区别
GitHub Actions 原来就有 job 并行(needs / matrix),但每个 job 是独立的 runner——有独立的文件系统、独立的环境变量、不共享 workspace。如果要在多个 job 之间共享依赖或者 artifact,需要 upload-artifact / download-artifacts,又多两步。
step 并行解决的是「轻量级独立任务,不值得拆 job,但串行又太慢」的场景。它在同一个 runner 里跑,共享 checkout 和缓存,没有跨 job 的传输开销。
选型原则很简单:
- 需要不同 OS、不同 runner 权限、不同失败隔离 → 用多 job 并行
- 共享环境、独立轻量任务、状态不多 → 用 step 并行
两个可以组合用:多个 job 并行跑,每个 job 内部再用 parallel 把独立步骤合并跑。
三步上手
第一步:确认现有 workflow 里哪些 steps 是独立的但目前串行跑的——lint + typecheck + test 三件套是最常见的候选。
第二步:给这三个 step 加上 background: true,在最后加一个 wait-all: true,然后在下面接真正的 build step。
第三步:跑一次看效果,重点关注 wall-clock 时间是否真的缩短,以及 logs 是否各自独立可读。如果某个 step 有副作用(需要共享 stdout、写入冲突等),退回串行即可。
10 月 1 日 GitHub Actions retention 变更(checks / workflow runs / statuses 统一 90 天清理)马上要生效了——趁着还在规划 CI 流程,顺手把那些串行跑的轻量检查并行化,也算在 retention 窗口缩小之前让 CI 多干点活。
- GitHub Actions step parallelism 从 2026 年 6 月 25 日正式支持,background/wait/wait-all/cancel 四个关键字全部在官方文档里有完整说明,可以直接上手。*
评论区
登录后可评论。