协程里的变量陷阱
先看脚本
IEnumerator YeildMethod()
{
yield return new WaitForSeconds(2f);
Debug.Log("2秒到了");
while (true)
{
yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.Space));
Debug.Log($"Space: go={gameObject.name} goId={gameObject.GetInstanceID()} compId={GetInstanceID()} fps{fps}");
}
}
private void Start()
{
startCount++;
// 在第一次 Update 之前调用一次
Debug.Log("<color=cyan>[3. Start]</color> - 逻辑开始");
Debug.Log($"Start called {startCount} times on {GetInstanceID()}");
StartCoroutine(YeildMethod());
}
private void Update()
{
fps++;
if (showRepeatEvents) Debug.Log("[Update] - 每帧逻辑更新");
if (NeedOnce)
{
Debug.Log($"Update 里的 fps {fps} Input.GetKeyDown(KeyCode.Space) = {Input.GetKeyDown(KeyCode.Space)}");
NeedOnce = false;
}
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log($"Update 里的 Space {fps}");
NeedOnce = true;
}
}
初步理解是按一下 space 会在update里和协程里均产生一次打印(update触发后故意多一次打印不在内,只是为了做参考)
但是实际上
按下时
fps-> down:
update Input.GetKeyDown(KeyCode.Space) = true
run coroutine Input.GetKeyDown(KeyCode.Space) = true
fps -> down+1:
update Input.GetKeyDown(KeyCode.Space) = false
run coroutine Input.GetKeyDown(KeyCode.Space) = true
最后一个协程判定里 Input.GetKeyDown(KeyCode.Space) = true 引起了两次协程打印。这就很有意思了,
查到资源 说的是:
这也解释了两个现象(全部统一了)
① 为什么协程里会触发两次?
帧 909:EarlyUpdate → true → 第一个 WaitUntil 通过
帧 910:EarlyUpdate → 仍然 true → 第二个 WaitUntil 再通过
Update 中已经是 false,但已经晚了
② 为什么 Update 永远只触发一次?
因为 Unity 在 Update 阶段 严格保证 KeyDown 只在一帧内为 true
而且只在 Update 的“第一次消费”有效
✅ 官方 & 工程级结论(非常重要)
Input.GetKeyDown 只应该在 Update() 中使用,
绝对不要直接作为 WaitUntil 的 predicate。
以上chatgpt的解析,待彻底搞明白了再来补充,暂时记录下
突然想到了一个关键字 Volatile 有点点感觉相似之处 比如下面代码
namespace ConsoleApp1
{
internal class Program
{
// 情况 A:不使用 volatile,会导致程序在 Release 模式下死循环
private static bool _stop = false;
// 情况 B:使用 volatile,程序会正常停止
// private static volatile bool _stop = false;
static void Main()
{
Console.WriteLine("线程正在启动...");
Task.Run(() =>
{
int i = 0;
// 在 Release 模式下,编译器可能将此处优化为 while(true)
// 因为它认为在此线程内部没有修改 _stop 的操作
while (!_stop)
{
i++;
}
Console.WriteLine("子线程检测到 stop 改变,已跳出循环。");
});
Thread.Sleep(1000); // 等待 1 秒
_stop = true;
Console.WriteLine("主线程已将 _stop 设置为 true。");
Console.ReadLine(); // 观察程序是否会打印出“已跳出循环”
}
}
}
然后release下运行之后
线程正在启动...
主线程已将 _stop 设置为 true。

浙公网安备 33010602011771号