Skip to content

并发模型总览:从七周七并发到游戏运行时 ​

Category ​

server / runtime 子树。节点定位:把并发立场先摆全(共享内存、不可变、身份状态分离、Actor、CSP、数据并行、数据分层)再判断——游戏工程不能只照书抄。runtime 子树入口。

Definition ​

如果第 5 章只讲“单线程、多线程、Actor、协程”几个词,范围其实是不完整的。因为并发问题从来不只有一种拆法,它背后至少有一整套不同立场:共享内存并发、不可变数据与纯函数、身份与状态分离、Actor、CSP、数据并行,以及把实时层和批处理层拆开的数据分层思路。

《Seven Concurrency Models in Seven Weeks》这类材料真正值钱的地方,不是让你照抄一套“圣经架构”,而是给你一张并发模型坐标图。游戏工程需要的,不是机械照搬,而是知道哪些模型适合做游戏主运行时,哪些只适合做局部工具,哪些更适合外围数据系统。

Problem ​

只讲「单线程、多线程、Actor、协程」几个词范围不完整:并发问题背后是一整套立场之争。立场不摆全,选型会退化成信仰之争——各自熟悉的立场各说各话,谁职级高听谁的。

先把常见并发立场摆出来 ​

如果按那条经典脉络来拆,最常见的七类模型可以概括成:

  1. 线程与锁。
  2. 函数式编程。
  3. 身份与状态分离,例如 STM / Refs / Agents 这一类思路。
  4. Actor。
  5. CSP,也就是 Communicating Sequential Processes。
  6. 数据并行。
  7. Lambda 架构。

这七类模型并不是互斥技术栈,而是七种不同的并发立场。它们关注的重点完全不同:

  • 有的在解决共享内存怎么管。
  • 有的在解决状态怎样不被随手改。
  • 有的在解决流程如何由多个顺序单元协同。
  • 有的在解决大批量计算怎样并行。
  • 有的在解决在线与离线怎样分层。

游戏工程为什么不能只照书抄 ​

游戏不是纯后端业务,也不是纯科学计算,更不是纯离线数据平台。它通常要同时处理:

  • 强实时链路。
  • 单写权威状态。
  • 高频对象更新。
  • 弱网、断线与重连。
  • 长线资产和外围服务。

所以游戏运行时经常会长成混合体:

  • 战斗主链路像单线程、Actor 或 Tick 驱动状态机。
  • 引擎热路径像数据并行和 Job System。
  • 会话和接入更像协程、状态机和 Channel。
  • BI、审计、推荐更像事件流和数据分层架构。

也就是说,游戏项目真正做的,通常不是“选择一个模型”,而是把不同模型放到不同位置。

一张更有用的总览表 ​

模型核心思想最强价值主要代价在游戏里更适合什么
线程与锁多线程共享内存,加锁保护直接、通用、兼容遗留系统争锁、抖动、时序难推理工具链、后台任务、部分服务基础设施
函数式编程不可变数据、纯函数、少副作用易测试、易推导、利于并行热路径拷贝和状态转移表达成本数值计算、规则求值、回放推导
STM / 身份与状态分离通过事务或引用治理共享状态降低共享写混乱高争用热路径成本高配置后台、低频协调逻辑、工具系统
Actor状态封闭,消息驱动,单 Actor 顺序执行边界清晰、单写者友好热点 Actor、消息风暴、跨 Actor 一致性房间、场景、玩家聚合、战斗权威
CSP / Channel顺序进程 + Channel 协作流程拆分清晰、组合性强Channel 设计不好会变成隐式阻塞网网关、会话、流水线、内部协调链路
数据并行同类数据批量并行处理吃满 CPU / SIMD / Job System不擅长复杂共享状态流程ECS、动画、寻路批处理、碰撞、渲染前处理
Lambda / 数据分层实时层 + 批处理层分工同时兼顾实时近似与离线精算双链路治理成本高BI、审计、推荐、风控、画像系统

这张表最重要的不是记术语,而是看清楚:同样叫“并发”,有的模型在治理共享状态,有的模型在治理流程,有的在治理批量计算,有的在治理数据分层。

哪几类模型更接近游戏主运行时 ​

如果只看核心游戏运行时,而不是整套项目所有系统,真正经常进入主链路的通常是:

  • 单线程 / 单写者思路。
  • Actor。
  • CSP / Channel 配合状态机。
  • 数据并行用于热路径批处理。

相反,下面这些更常见于局部层或外围层:

  • 函数式思路,用于规则求值、回放和配置处理。
  • STM / 身份与状态分离,用于工具、后台和低频共享状态。
  • Lambda / 数据分层,用于 BI、风控和离线分析。

这个区分很重要,因为很多团队的问题不是模型不知道,而是把适合外围系统的模型,错放到了高时序敏感的核心链路上。

一个更贴近游戏项目的判断方法 ​

讨论并发模型时,先不要问“哪个先进”,先问下面几件事:

  • 这份状态是否允许多人同时写。
  • 这条链路是否必须保持顺序可推理。
  • 这里的主要压力是共享状态冲突、异步流程复杂,还是批量计算吞吐。
  • 故障最怕的是不一致、抖动,还是吞吐不够。

这些问题答清楚以后,大多数模型选择其实会自然收敛。

常见误区 ​

  • 把并发模型理解成语言或框架口味之争,而不是状态治理方式之争。
  • 试图让一个模型覆盖整套游戏系统,结果主链路和外围链路都不合适。
  • 只看理论并行能力,不看时序可推理性、恢复难度和线上排障成本。

这一页真正想建立的,不是哪种模型最好,而是一套判断并发问题该落在哪一层的坐标系。

实战案例:一次「一个模型打天下,全服越改越抖」的并发立场错配排查 ​

案例为教学示例(非摘自真实项目),数字经过取整简化。它把本页「先把常见并发立场摆出来」「一张更有用的总览表」「一个更贴近游戏项目的判断方法」用到深水区:选的是「最先进的」,抖的是主链路——这是立场没摆全、模型错放位置的排查,也是「先问问题再选模型」的完整展开。

背景与现象:某团队用一套 Actor 框架覆盖全服 4 类链路(战斗主链路、引擎热路径、会话接入、BI 审计),理由是「Actor 最先进」。战斗高峰时热点场景 Actor 单帧排队 35 毫秒,帧 P99 涨到 48 毫秒(预算 16 毫秒);同一批 BI 日更任务和对局结算挤在同一条在线链路上,结算延迟 6 小时。

第一步:现象与口径——先分清哪条链路在抖 ​

影响面口径:帧 P99 48 毫秒超预算三倍,掉帧投诉集中在战斗高峰;结算与日更走同一套 Actor 调度,晚高峰互相挤占。

选型口径:选型会议记录里只有「哪个更先进」的争论,没有一条针对状态、顺序、压力与故障的判断——四问一个都没答。

第二步:分层归因——按本页判断方法四问排查 ​

text
全服越改越抖归因  占比
  一套模型覆盖 4 类链路、外围模型放进主链路    56%
  热点 Actor 单写排队                        27%
  BI 审计批处理挤在在线链路                  17%

逐项比对:四类链路被同一套模型盖住,本页说得直白——很多团队的问题不是模型不知道,而是把适合外围系统的模型,错放到了高时序敏感的核心链路上;场景主 Actor 单写排队;BI 日更与结算挤在线上链路。三类都在,错放位置那条占比最大。

第三步:根因——立场没摆全,选型退化成信仰之争 ​

只讲「单线程、多线程、Actor、协程」几个词,立场不摆全,选型会退化成信仰之争——各自熟悉的立场各说各话,谁职级高听谁的。四个判断问题——这份状态是否允许多人同时写、这条链路是否必须保持顺序可推理、主要压力是共享状态冲突还是异步流程还是批量吞吐、故障最怕不一致还是抖动还是吞吐——一个都没答,模型选择没有收敛依据。

第四步:处置与回填——按坐标系重新摆位置 ​

  1. 立场摆全:把七类并发立场和总览表搬进选型会,每个候选模型对位「最强价值 / 主要代价 / 更适合什么」三栏。
  2. 主链路收口:战斗主链路只保留单写者、Actor、状态机与数据并行;BI、审计、推荐迁出到 Lambda / 数据分层链路。
  3. 四问前置:任何并发选型先答四个判断问题,答不清楚不进评审。

复核账:帧 P99 48 毫秒 → 16 毫秒;热点 Actor 单帧排队 35 毫秒 → 4 毫秒;结算延迟 6 小时 → 40 分钟;4 类链路全部过四问评审。

回填清单:

回填项动作回填处
立场坐标图七类立场逐条对位「最强价值 / 主要代价 / 更适合什么」,再进选型一张更有用的总览表
主链路候选边界战斗主链路只进单写者、Actor、CSP 状态机与数据并行哪几类模型更接近游戏主运行时
四问定收敛选型前先答多写、顺序、压力、故障四问一个更贴近游戏项目的判断方法
误区评审哨兵禁口味之争、禁一模盖全服、禁只看理论并行常见误区

案例的三个教训 ​

  1. 先进不等于匹配。并发模型是状态治理方式之争,不是语言或框架口味之争,答不出四问的选型都是信仰之争。
  2. 错放位置比选错模型更常见。适合外围系统的模型放进高时序敏感的主链路,抖动是必然结果。
  3. 混合体才是常态。游戏项目真正做的不是选一个模型,而是把不同模型放到不同位置,主链路和外围各归其位。

Used By ​

并发方案选型会议的立场盘点;团队公共语言对齐。

  • skynet(commit 64391f7):线程池 × 服务队列两级调度——worker 线程从全局队列取出某个服务的队列后连续分发(skynet-src/skynet_start.c:157、:167),服务上下文把队列排空、逐条调用消息处理(skynet-src/skynet_server.c:283-289)。服务数量再多,同一服务同一时刻也只有一个线程在跑:「单线程可推理」是调度结构给的,不是约定给的

关系链:立场罗列 → 游戏工程特殊性 → 总览表 → 主运行时候选 → 判断方法。上游:无(runtime 入口)。下游:runtime/01(三种状态组织哲学)。

Reference ​

本页为工程经验归纳;页内实战案例为教学示例(数字取整简化)。

游戏知识体系