zoukankan      html  css  js  c++  java
  • 遍历删除

    今天想实现个小功能:战斗结束时,后台先立即退房间
    • 退房间逻辑包含了:所有人退光后,销毁房间
    • 销毁房间,会同步销毁GameWorld
    在OnPlayerDie回调中加了一句代码,导致连续出现四处宕机bug~太牛逼了
    • Bug分属四处不同模块
    • 均是同种原因:遍历中途,删元素,上层持有的引用失效
    很好修复,但这不是重点。重点是,这bug同时隐匿在
    • 系统最底层,碰撞检查器,遍历调用OnTriggerEnter
      • OnTriggerEnter 参数中的某些对象已被销毁
    • 系统底层,GameWorld update
    • 系统底层,GameWorld内置计时器
      • GameWorld都没了,还在跑遍历
    • 业务层,OnPlayerDie遍历队伍,通知调用链中,触发退队,迭代器失效
    分别不同人写的模块,且不做本次业务改动,跑得很稳定
    • 也就是说:在写模块时,开发者没想到这bug。这里有两层缘故:
    • 一是,迭代遍历,太常见,太顺手了 …… 依赖个人警惕,非常不靠谱
      • 交叉review靠谱点,但现实里review是需要成本的,极少团队愿意支付这个成本
    • 二是,上述模块均带回调事件
      • 回调链,究竟多长,会做啥 …… 不受起初写该模块人的控制
      • 是个需求上就非内聚的东西
     
    本质是:持有着某对象的引用,调用事件函数后,共享出去了它的操作权,但写的人忘了它会被改,更不能期望别人会主动通知。
     
    根本解决方案:支持一套迭代安全的容器模板,仅高内聚的模块允许使用原生迭代器
    • 预想的坑:回调链中,可能再次调用到遍历(函数重入),常规类中记个游标的方式,也是不安全的
    • 标记删除,或许更好
     
    【二版修正】
    GameWorld定时器遍历宕机,不属于“迭代遍历删元素”bug,它是因为定时器回调里销毁了GameWorld
    是个架构问题,作为最底层的GameWorld一类的,它的真正销毁、删元素权限,不该暴露给业务上层
    • 给上层的接口,都是标记式的
    【扩展联想】
    • 析构比构造难多了
      • c艹的确定性析构,需要开发者准确掌握资源回收的时序
      • 业务层的资源回收步骤,往往带着很长的调用链
    • 写这个,有种网上描述屎山代码的体验~改个小地方,崩一片
      • 如若是线上版本开发,做这个的程序,会是啥心态
      • 作为管理者,你会悉心了解问题本质,企图改掉它们吗?大概率不会
      • 完整解决它的可能性极低,大约会搞成延时删除、标记删除、拷贝遍历之类的
      • 不解决根本问题,下一个不注意的仍会踩坑 …… 避开坑,而不是填坑
    • c++是个很棒的语言,但得重新审视下它做业务开发的性价比
      • 其它语言碰见这bug的印象淡许多
      • c# foreach 遍历删会报容器被修改的异常
      • go for range 删不会报错
      • 带gc的语言,这方面表现好得多。但一样有坑点
        • 比如遍历中途加元素,引发扩容
        • 你还在遍历的,仍是老的内存块,老的元素
    【网友讨论】
    • 定时器,最优方案是,遍历实际只是先选出待执行的callback,收集
      • 遍历收集结束,再单独执行那一小批
      • 一边遍历一边执行,显然是不对的
  • 相关阅读:
    Linux HugePages及MySQL 大页配置
    tcp短连接TIME_WAIT问题解决方法大全
    从问题看本质: 研究TCP close_wait的内幕
    tcp_tw_recycle和tcp_timestamps的文章汇总
    MYSQL博客
    Tcp_tw_reuse、tcp_tw_recycle 使用场景及注意事项
    net.ipv4.tcp_tw_recycle
    TIME-WAIT和CLOSE-WAIT
    mysql 源码调试方法
    mysqldump 备份原理9
  • 原文地址:https://www.cnblogs.com/3workman/p/14872179.html
Copyright © 2011-2022 走看看