HOME / 第 6 部分 · 数据与进阶开发 / 第 69 课
MODULE 6 · 数据与进阶开发
第 69 课 · 多线程
🎯 本课学习目标
- 理解多线程的意义与风险
- 用 Thread 执行后台任务
- 用信号安全回传结果
复杂计算(寻路、生成地图)卡主线程?本课用多线程处理,并学会安全通信。
📖 详细教学步骤
多线程是什么?概念
- 主线程:处理游戏逻辑与渲染
- 其他线程:并行跑耗时任务(文件、计算、网络)
- 好处:不阻塞主线程,画面不卡
- 风险:同时访问共享数据会冲突
💡 简单项目尽量少用,只有明确卡顿才用。
基础线程代码
Thread 执行一个函数:
GDSCRIPTextends Node
var thread: Thread
func _ready():
thread = Thread.new()
# 后台执行 heavy_calc(不阻塞主线程)
thread.start(heavy_calc)
func heavy_calc():
var sum = 0
for i in range(10000000):
sum += i
print("后台计算完成:", sum)
# 注意: 线程里不能直接操作场景节点!
func _exit_tree():
if thread:
thread.wait_to_finish() # 退出前等待线程结束
线程传参与返回代码
传参:
GDSCRIPTthread.start(heavy_calc.bind(100, "参数"))
func heavy_calc(count: int, tag: String):
print(tag, count)
安全回传结果(信号 + 主线程)重点
线程算完要把结果交回主线程用 call_deferred:
GDSCRIPTextends Node
var thread: Thread
var result: float = 0.0
func _ready():
thread = Thread.new()
thread.start(compute_pi)
func compute_pi():
var pi = 0.0
for i in range(1000000):
pi += 4.0 * (1 if i % 2 == 0 else -1) / (2 * i + 1)
# 用 call_deferred 回到主线程更新
call_deferred("_on_done", pi)
func _on_done(pi: float):
result = pi
$Label.text = "π ≈ " + str(pi) # 主线程才能改 UI
thread.wait_to_finish()
thread = null
注意线程安全警告
- ❌ 线程里改场景节点/UI → 崩溃
- ❌ 线程里读字典同时主线程写 → 竞态
- ✅ 线程只算数据,用 call_deferred 回主线程更新
- ✅ 或使用 Mutex/Semaphore 保护共享数据
实用场景应用
适合用多线程的:
- 程序化生成大地图/噪声
- 大型寻路计算
- 文件压缩/加密
- 网络数据解析
简单计算别用线程,开销反而更大。
⚠️
线程里操作节点崩溃?
Godot 线程不安全。所有场景/UI 操作必须回主线程(call_deferred 或信号)。
⚠️
退出时报错线程未结束?
场景退出前一定要 thread.wait_to_finish(),否则 Godot 报错。
🧪 动手试一试
🏋️ 课后练习
用线程后台计算 1 到 1 亿的和,计算时界面保持流畅(用 Label 每秒刷新当前帧数),算完用 call_deferred 显示结果。
线程算数 → call_deferred("_on_done", sum) → 主线程更新 UI。
💡
能用单线程解决就别上多线程,复杂度和 bug 会翻倍。
💡
需要共享数据时,用 Mutex(互斥锁)保护。
🎓 本课小结
多线程:Thread.start 后台跑函数,计算不阻塞主线程;结果用 call_deferred 安全回主线程更新;线程里严禁直接操作节点;退出前 wait_to_finish。