第九章 冲刺期的第一个Bug (第2/3页)
音。下雨时,用泛音模拟雨滴。打烊时,拉长,淡出。”
“很好。”李君宪说,“就用这个方向。但注意内存,别做太复杂的变奏。”
“明白。”
会议结束。李君宪看着记满三页的待办事项,感觉太阳穴在跳。时间、内存、性能、兼容性、Bug……每个问题都像一根绳子,慢慢绞紧。而他们手里只有一把生锈的剪刀。
他站起来,走到水房,用凉水冲了把脸。镜子里的人,眼睛里有血丝,下巴冒出胡茬,T恤领口有汗渍。二十一岁的外表,三十岁的疲惫。
回到座位,他打开邮箱。有一封新邮件,来自“IGF China组委会”,标题是“关于作品提交流程的补充说明”。
他心里一紧,点开。
邮件很长,主要是技术规范:可执行文件不能超过50MB,必须能在Windows XP SP2上独立运行,不能依赖任何第三方库除非自带,必须提供卸载程序,等等。最后一段用加粗字体写着:
“特别注意:学生组作品,必须由在校学生完成。团队中如有已毕业人士参与,需提供详细分工说明,并确保核心创意和主要工作量由在校学生完成。组委会保留审核资格的权利。”
他反复读了三遍。核心成员里,陈末大四,即将毕业,但还算在校生。叶晚大三,林薇大三,苏语大三,他自己大三。没问题。
但“主要工作量由在校学生完成”——如果组委会认为陈末的渲染框架工作量太大,算不算“主要”?如果叶晚的母亲帮忙绣了某个纹理(虽然不太可能),算不算“非学生参与”?这些模糊地带,都可能成为被拒的理由。
他把邮件转发到群里,附言:“大家看看最后一段。注意规避风险。陈末,你的渲染框架,能提供详细的代码注释,证明是你独立完成的吗?”
陈末几分钟后回复:“能。我写代码习惯好,每个模块都有文档。另外,我可以提供学生证扫描件和在读证明。”
“好。大家也都准备好学生证明,以防万一。”李君宪敲下这行字,忽然觉得有点荒谬。他们还没做出像样的Demo,就开始担心参赛资格的问题了。
但这就是现实。理想需要现实铺路,哪怕这条路布满碎石。
他关掉邮箱,继续对付那个时间Bug。加了更多日志,在可能出问题的锁同步处埋了十几个断点,重新编译,运行测试程序。
这一次,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。二十次,没出现。五十次,还是没出现。
他长舒一口气,把修复方案提交到SVN,在群里@陈末:“时间Bug可能解决了,是锁顺序的问题。你那边跑一下压力测试看看。”
陈末半小时后回复:“跑了二百次,零复现。应该是修了。但合并逻辑我优化了一下,减少了锁的持有时间,性
(本章未完,请点击下一页继续阅读)