两天时间“帮”同事解了一个多线程问题
下游就是这么苦,上游一句“我这没事啊”,问题就成了你的了。拼了老命、挠破头皮地搞了一天,发现实在是找不出问题,还得想尽办法把问题定位到上游的“黑盒盒”里,人家才会不疾不徐地开展工作。
遇到这种同事能怎么办?我只能在心里暗爽:嘿嘿,又“帮”儿子解决了一个 bug。
这次的故事是这样的。
同事搞了工作线程,收尾没搞好,会在我调了逆初始化接口后依然回调我的代码,导致崩溃。
改了一版之后给我,Unity 退出时不知道什么原因会无限等待这个线程退出,导致 freeze。
windbg 调试发现工作线程依然在。
同事说你不传回调试试,我不传果然就正常退出了——可我的回调就是一个空方法啊!
同事开启嘲讽技能,说早说是你的问题了,blah blah。
我排查回调相关代码,不觉得有任何问题—— PInvoke 的写法已经在别的项目里验证过的。
回到 windbg,发现即便我不传回调,freeze 时其工作线程依然存在。一个合理的怀疑是:只要工作线程回调调用过 C# 托管代码,Unity 便会跟踪、等待此线程。
社区寻求答案。虽然没找到官方说明,也有一些类似讨论。
先不管 Unity 的机制怎么样,你在逆初始化后线程不退出肯定是不对的。深挖这点发现,这货直接把工作线程搞成了全局的!怎么发现的呢?我有可重入需求,发现每次初始化时线程 id 总是同一个……
基本可以确定 freeze 原因了:你搞了个全局线程,没有释放时机,导致 Unity 进程退出时无限等待。
同事给了个修复包,freeze 解决了。
就事论事的说,多线程的生命周期管理的确是有些难度,即便是熟知原理,稍不注意也会搞出各种问题—尤其是增量开发情况下,对原有逻辑掌握不够就开工,极易因为意想不到的运行时序导致问题。
抛开这事儿本身来说,也充分说明了是 bug 就总有会暴露的一天。必须摆正心态,刨根问底,抓出真凶。企图以侥幸心理蒙混过关,既失道德,又败人品。
于我也很有收获,Unity 的线程管理这块要研究,看看到底是什么情况下会等待工作线程。