• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

遍历删除

开发技术 开发技术 3小时前 3次浏览
今天想实现个小功能:战斗结束时,后台先立即退房间
  • 退房间逻辑包含了:所有人退光后,销毁房间
  • 销毁房间,会同步销毁GameWorld
在OnPlayerDie回调中加了一句代码,导致连续出现四处宕机bug~太牛逼了
  • Bug分属四处不同模块
  • 均是同种原因:遍历中途,删元素,上层持有的引用失效
很好修复,但这不是重点。重点是,这bug同时隐匿在
  • 系统最底层,碰撞检查器,遍历调用OnTriggerEnter
    • OnTriggerEnter 参数中的某些对象已被销毁
  • 系统底层,GameWorld update
  • 系统底层,GameWorld内置计时器
    • GameWorld都没了,还在跑遍历
  • 业务层,OnPlayerDie遍历队伍,通知调用链中,触发退队,迭代器失效
分别不同人写的模块,且不做本次业务改动,跑得很稳定
  • 也就是说:在写模块时,开发没想到这bug。这里有两层缘故:
  • 一是,迭代遍历,太常见,太顺手了 …… 依赖个人警惕,非常不靠谱
    • 交叉review靠谱点,但现实里review是需要成本的,极少团队愿意支付这个成本
  • 二是,上述模块均带回调事件
    • 回调链,究竟多长,会做啥 …… 不受起初写该模块人的控制
    • 是个需求上就非内聚的东西
 
本质是:持有着某对象的引用,调用事件函数后,共享出去了它的操作权,但写的人忘了它会被改,更不能期望别人会主动通知。
 
根本解决方案:支持一套迭代安全的容器模板,仅高内聚的模块允许使用原生迭代器
  • 预想的坑:回调链中,可能再次调用到遍历(函数重入),常规类中记个游标的方式,也是不安全的
 
【扩展联想】
  • 析构比构造难多了
    • c艹的确定性析构,需要开发者准确掌握资源回收的时序
    • 业务层的资源回收步骤,往往带着很长的调用链
  • 写这个,有种网上描述屎山代码的体验~改个小地方,崩一片
    • 如若是线上版本开发,做这个的程序,会是啥心态
    • 作为管理者,你会悉心了解问题本质,企图改掉它们吗?大概率不会
    • 完整解决它的可能性极低,大约会搞成延时删除、拷贝遍历之类的
    • 不解决根本问题,下一个不注意的仍会踩坑 …… 避开坑,而不是填坑
  • c++是个很棒的语言,但得重新审视下它做业务开发的性价比
    • 其它语言碰见这bug的印象淡许多
    • c# foreach 遍历删会报容器被修改的异常
    • go for range 删不会报错
    • 带gc的语言,这方面表现好得多。但一样有坑点
      • 比如遍历中途加元素,引发扩容
      • 你还在遍历的,仍是老的内存块,老的元素

程序员灯塔
转载请注明原文链接:遍历删除
喜欢 (0)