g。加了更多日志,在可能出问题的锁同步处埋了十几个断点,重新编译,运行测试程序。
这一次,Bug在第三次测试时就出现了。世界卡在0.3倍慢放,李师傅的动作像在水里走路,一帧一帧地挪。日志文件滚屏,他一行行看,眼睛发酸。
忽然,他注意到一行奇怪的日志:
[TimeSystem] Thread conflict detected at timestamp 120.5s.
[RenderThread] Acquired lock at 120.5001s.
[TimeThread] Acquired lock at 120.5001s.
时间戳完全一样。两个线程,在同一毫秒内,获取了同一把锁。理论上不可能,除非系统时钟精度不够,或者锁的实现有漏洞。
他查代码。用的是标准的CRITICAL_SECTION锁,Windows自带的,不应该有问题。除非……他想到一个可能性:在“无事可做”状态下,时间系统会分裂成两条时间轴,每条时间轴都有自己的锁。当玩家退出静止状态,两条时间轴要合并时,需要同时获取两把锁。如果获取顺序不对,可能死锁。
他翻到合并逻辑的代码。果然,写成了:
lock(timeLock_室内);
lock(timeLock_窗外);
// 合并逻辑
unlock(timeLock_窗外);
unlock(timeLock_室内);
而另一个地方,渲染线程更新窗外光影时,顺序是:
lock(timeLock_窗外);
lock(timeLock_室内);
// 更新逻辑
unlock(timeLock_室内);
unlock(timeLock_窗外);
经典的死锁条件:线程A锁了1,等2;线程B锁了2,等1。平时很难触发,因为两个线程很少同时卡在这个点上。但在“无事可做”状态下,时间系统频繁分裂合并,渲染线程又要频繁更新窗外光影,撞上的概率就大了。
他修改代码,强制统一锁的获取顺序:永远先锁室内,再锁窗外。重新编译,运行测试程序。
跑完十次,没出现Bug。二十次,没
本章未完,请点击下一页继续阅读!