1. 同步与异步:从“排队买奶茶”到“点外卖”
咱们写代码,尤其是做游戏开发,最怕的就是“卡”。你正玩得起劲,画面突然一帧一帧地动,或者点个按钮半天没反应,这体验一下子就垮了。这背后,往往就是同步和异步编程模式没选对惹的祸。今天,我就用最生活化的例子,带你彻底搞懂它们,并在Unity里玩转它们。
想象一下你去买奶茶。同步就像你在柜台前排队,你必须站在队伍里,眼巴巴地看着店员做完前一个人的奶茶,才能轮到你点单。这期间你不能去隔壁买个鸡蛋仔,也不能刷手机回消息,只能干等着。代码里的同步也是如此,TaskA() 不执行完,TaskB() 就别想开始,主线程(可以理解为游戏运行的主干道)被彻底“堵死”了。
而异步呢,就像你用手机App点外卖。你下单支付后,App会告诉你“商家已接单”,然后你就可以把手机放一边,该干嘛干嘛去了——继续工作、刷视频、甚至打一局游戏。等外卖小哥到了,App会“通知”你:“您的外卖已送达”。在这个过程中,你没有在店门口傻等,你的时间被充分利用了。对应到代码,就是主线程发起一个耗时任务(比如加载一个大资源)后,不用等它完成,立刻回头去继续渲染游戏画面、处理玩家输入。等那个耗时任务在“后台”干完了,再通过某种方式“回调”通知主线程:“嘿,东西好了,你来处理一下结果吧。”
所以,同步的核心是 “顺序等待,阻塞执行” ,逻辑简单直白,但容易造成卡顿。异步的核心是 “非阻塞,事件驱动” ,效率高、响应快,但逻辑会变得复杂一些。在Unity里,我们追求的就是在保持游戏流畅(60帧甚至更高)的前提下,处理好所有事情,异步编程就是实现这个目标的超级武器。
2. 同步编程:简单直白,但也是“卡顿”的元凶
2.1 同步到底是怎么“堵车”的?
在Unity的脚本生命周期里,Start()、Update()、FixedUpdate() 这些方法默认都是同步执行的。这意味着,写在它们里面的代码会一行接一行、严丝合缝地跑完。
我踩过的一个经典坑是在 Start() 里同步加载一个巨大的场景资源。代码看起来人畜无害:
void Start()
{
Debug.Log("开始加载资源...");
// 假设这是一个非常耗时的同步加载方法
GameObject hugeMap = LoadHugeMapSynchronously("Map_Desert");
Debug.Log("资源加载完成!");
// 初始化地图逻辑...
}
运行起来,游戏启动后,画面会完全冻结,控制台卡在“开始加载资源...”这里,好几秒甚至十几秒后,才突然打印出“资源加载完成!”,画面也随之恢复。在这段“黑暗时间”里,Unity的主线程被 LoadHugeMapSynchronously 这个函数完全霸占,它干不完活,渲染、物理计算、输入检测所有这些事都得停下来等它。玩家看到的就是游戏“未响应”。
2.2 多线程里的同步:只是换了个车道堵
你可能会想:“那我开个新线程去做耗时任务,不就不卡主线程了吗?” 没错,新线程本身确实不阻塞主线程。但是,线程内部的代码默认依然是同步的。
using System.Threading;
using UnityEngine;
public class Example : MonoBehaviour
{
void Start()
{
Debug.Log("主线程:我要开个新线程去干活了!");
new Thread(DoHeavyWorkInThread).Start();
Debug.Log("主线程:我继续happy,不受影响~");
}
void DoHeavyWorkInThread()
{
Debug.Log("子线程:开始第一步复杂计算...");
Thread.Sleep(3000); // 模拟耗时计算,阻塞子线程自己3秒
Debug.Log("子线程:第一步完成,开始第二步...");
// 第二步计算...
}
}
在这个例子里,主线程确实流畅,两行日志瞬间打印完。但子线程 DoHeavyWorkInThread 自己内部,Thread.Sleep(3000) 会让这个子线程自己挂起3秒,第二步计算必须等这3秒结束后才能开始。这就是线程内的同步。它解决了主线程卡顿的问题,但没解决任务本身执行效率低下的问题。如果这个耗时任务可以拆分成多个小任务,我们或许能利用等待的时间做点别的,这就需要异步思维了。
注意:在Unity中,
Thread.Sleep会阻塞当前线程,在主线程里用绝对是灾难。即使在子线程中用,也要谨慎,因为它会让该子线程停止响应。
所以,同步模式总结下来就一句话:代码顺序执行,任务必须完成才能进入下一步。 它适合那些瞬间就能完成的操作,比如简单的数学运算、变量赋值、调用一个立刻返回结果的方法。一旦遇到文件读写、网络请求、复杂寻路计算,同步就会成为性能瓶颈。
3. 异步编程的两种面孔:协程与多线程
在Unity和C#的世界里,实现异步主要有两大门派:协程 (Coroutine) 和 多线程 (Threading)。它们的目标一致——避免阻塞,但实现的哲学和适用场景天差地别。
3.1 协程:单线程下的“时间管理大师”
你可以把协程理解为主线程这个“单核CPU”上的超级时间管理术。


316

被折叠的 条评论
为什么被折叠?



