你以为 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 四个关键字全部在官方文档里有完整说明,可以直接上手。*

评论区

0 条评论

登录后可评论。