Continue
三) 去年最经典的Bug
这个Bug的父亲不是我,是我的一个同事, 不过最终bug是被我抓到,也算是非常经典的一个bug,.
当时我在SBXF开发那个该死的休闲游戏.大家知道动态加载的资源的I/O操作需要消耗大量的时间,如果在游戏正常运行中,突然出现许多需要加载的资源,并且不对其进行处理的话,显然会造成游戏的"卡",也就是画面突然停顿的现象.解决这种方法,最容易想到就是多线程问题.
但是多线程两个非常大的问题是同步和3D设备的占用问题(OpenGL尤其麻烦).所以最终我没有采用多线程.而是采用了单线程,但是把需要加载的动作做成队列,采用定额加载的方法. 也就是说在一个updateFrame调用里.最多只加载一定数量的资源.
这个实现起来是比较容易的. 当时项目组的石头同志,很快就实现了这个功能, 我们需要动态加载的就是一些魔法,炮弹等资源. 于是它在创建这个魔法的时候就检测魔法用到的模型等资源是不是存在,如果不存在就把魔法类的一个成员(这个成员是对模型资源的包装)放到加载队列中. 这样.加载队列就自动会去加载资源. 而魔法类因为使用了资源管理系统,它会智能的在模型等数据被加载成功后才显示出来.
程序在开始的时候没有什么问题. 一两个人进入游戏发发炮弹和魔法,没有什么问题. 可是后来人一多的时候就出现问题了: 炮弹一发多了程序就会挂.
挂的地方还非常精彩. 当时石头同志放到加载队列的结构体大约是这样的.
struct sLoadInfo
{
vModelManager*m_ModManger:
vModelPlayer*m_ModelPlayer;
}
挂的时候,调用的是ModelPlayer的getModel. 但是只要往里一跟,调用堆栈进入的不是ModelPlayer的getModel,却成了ModelManager的load();并且显示出来传递进来的参数都是错的.
实在是非常没有天理!!!!
看到这样的call stack , 石头同志严重怀疑我的模型系统出了问题.我也埋头找我的bug, 这头一埋就是两个多月. 到后来 游戏在公司内部开始测试的时候,这个bug依然没有解决. .而且似乎我也解决不了了.
有一天.偶然机会我抓到了一个bug, 石头同志到处乱delete那些魔法对象. .....那么不会也是同样的不负责的delete 操作而引起那个callstack的破坏吗? 于是我相信分析了一下流程(就是上面提到的):
当用户多起来,发炮弹勤快一点以后, 加载队列里的东西比较多,可能加载延迟比较长.而这个时候,经过了几秒,魔法已经在地图上消失了. 对象已经被释放掉了.但是sLoadInfo却还留在队列里. 这个时候再去调用他们的函数,出什么现象都是正常的. 而人数比较少.发炮弹比较少的时候, 是不会出现的.
于是我把魔法时间改成了很小的一个数,重新测试了一下.果然,挂的就是不是偶尔了.而是经常挂了!!!!
一个2个月未解决的bug,,其实原来是这么简单:(
反思:
抓别人代码的bug的时候,要学会分析他的思路和流程.
单线程的程序也是可以出同步问题的.
删除对象一定要负责... 要删除的时候,设置一个标记.由一个地方统一删除,并应该能正确通知到所有的使用了该对象的地方. 最好的方法就是使用引用计数之类的方法. 只调用release() 具体什么时候delete由release内部决定!