说来惭愧。每次看到这个问题,就只能想到“进程拥有资源,线程拥有cpu”之类一点不严谨的描述,两年前学的408现在忘得差不多了,导致每次看到这些问题,总感觉有万匹飞奔的载着408知识的野马从我的大脑皮层轻轻掠过。

首先,想得确实没错。进程是资源分配的基本单位。资源有什么?第一个一定是有自己的虚拟地址空间,独立的虚拟内存,然后就是文件描述符表,环境变量,信号处理表等等,通过PCB记录资源的状态。主要目的就是提供一个隔离开的执行环境,一个进程停止或崩溃不会影响到其他进程。

线程,是cpu调度的基本单位。同一个进程内的线程是共享进程内的资源的。每个线程又有什么资源?线程需要有自己的id,程序计数器,寄存器集合以及自己的栈空间(重点),由TLB(线程控制块)来记录这些调度所需的信息。

注意,C++中,一个线程的崩溃可能会导致整个进程的终止。(可以引出线程的健壮性没有进程的高)

这是因为操作系统以进程为基本管理单元:所有线程共享同一地址空间和资源,当一个线程因段错误或未捕获异常等致命错误崩溃时,会触发进程级别的信号(如SIGSEGV),操作系统为避免数据不一致或资源泄漏,默认会终止整个进程。虽然可以通过信号处理或线程隔离技术尝试局部处理,但本质上操作系统将进程视为不可分割的整体,一个线程的致命错误被视为整个进程的失效。

在游戏开发中,选择多进程还是多线程是一个基于隔离性、性能、复杂性和稳定性等多维度考量的架构决策。其核心逻辑是将需要紧密协作、高频数据共享、且对延迟极度敏感的核心游戏循环置于多线程模型之下;而将那些需要独立生命周期、强故障隔离、或涉及外部安全性的边界系统部署为多进程。

游戏的核心运行时引擎几乎无一例外地重度依赖多线程,这是由现代游戏的性能需求决定的。游戏帧循环(Game Loop)是一个严格的实时系统,必须在大约16.6毫秒(对应60帧/秒)内完成逻辑更新、动画计算、物理模拟、场景管理、渲染命令提交等一系列繁重工作。为了将这些工作分摊到多个CPU核心上,引擎会采用精细的多线程分工。例如,主线程(通常称为游戏线程)负责驱动游戏逻辑、脚本执行和动画状态机;一个或多个渲染线程负责处理图形API调用、资源状态管理和命令缓冲区;独立的音频线程负责解码、混音和三维音效处理;庞大的后台线程池则用于处理异步资源加载、着色器编译、数据解压、以及通过任务并行系统(如虚幻引擎的Task Graph、Unity的Job System)来并行执行大量的同质计算,如视锥体剔除、粒子系统更新、蒙皮矩阵计算等。这些线程之间需要毫秒级的同步和极高频的内存数据交换(如传递变换矩阵、可见物体列表),使用线程共享内存是唯一能满足这种性能要求的方式。

然而在游戏开发和运行的特定边界场景下,多进程架构则展现出不可替代的价值。首要场景是开发工具链与运行时分离。游戏编辑器(如Unreal Editor、Unity Editor)通常运行一个独立的游戏进程(Play-in-Editor)来预览游戏效果。这种进程级隔离至关重要:当测试的游戏崩溃时,它只会带走这个预览进程,而宝贵的编辑器工作状态(如正在编辑的关卡、未保存的资源)得以完好无损,极大提升了开发效率和稳定性,并支持安全的热重载功能。第二个关键场景是安全与反作弊。在在线多人游戏中,反外挂模块(如反作弊客户端)通常以一个高权限的独立进程运行,通过进程间通信监控游戏主进程的内存和操作。这种隔离增加了恶意软件进行代码注入和数据篡改的难度。第三个场景是模块化与可维护性。一些大型游戏或引擎允许将相对独立的功能模块(如特定的物理模拟器、语音聊天服务、视频录制工具)作为插件进程运行。这样单个插件的崩溃或更新无需重启整个游戏主进程。在服务器端架构中,多进程也更为常见。一个大型多人在线游戏的服务器集群可能由多个专有进程组成,例如独立的登录认证进程、世界逻辑进程、数据库代理进程等,这种分离有助于资源管理、独立扩展和故障隔离。

后面可以开始了解一下UE多线程的一些概念了:

[原创]UE基础—多线程(一) - 凌泽的文章 - 知乎
https://zhuanlan.zhihu.com/p/553957069

现代C++的内存模型和高性能的多线程编程:https://skyscribe.github.io/post/2019/11/04/cpp-memory-model-and-order/