最近情绪有点失控,跟很欣赏的同事吵架了,真难过。有些话,可能真的只有吵架才会说出口,只好安慰自己,这未尝不是一次难得的交流。究其原因,就是一段代码由我开发,他接手维护,然后交给我继续跟进;最近频繁报 bug,他觉得在替我擦屁股,收拾代码,我也觉得我在替他擦屁股,大家心里有气,但是谁也没有说出口。多点面对面交流,多点讨论,工作压力小一点,可能就可以避免了。

最近修复的 dbd 的 bug,一个是字符串管理上面的, 上一篇博文 已经提到了 mapping_to_bson 的,这周还发现了 bson_to_mapping 过程中的问题。由于 dbd 的对象管理采用了 LPC 虚拟机带的那套,包括 hash 表和字符串管理,而这套东西默认是在单线程运行的,没有考虑线程安全,所以 dbd 作为唯一一个多线程运行的进程,就有线程安全的隐患了。我翻看了原始实现,在替换 mongodb 前,dbd 实现反序列化的时候,很机智的避开了共享字符串,不管这个用作 mapping key 的字符串是否已经使用过,直接从系统里分配内存。我理解另一位同事用共享字符串的理由,维基里提到 string interning,就是为了节省内存和时间。因为 dbd 作为玩家数据的缓存,存储的玩家一定有很多 key 是相同的,这部分内存如果节省下来应该蛮可观的。为了线程安全,可能需要牺牲掉这个优化了。

另外一个是 mapping 结点的管理。新建 mapping 结点的时候,会从一个 mapping 空闲结点池里取,如果没有空结点则 malloc 一个新的,放到池里,最后从池里取。由于这个空结点池是全局的,没有加锁保护,同样会导致多线程上出现问题。如果不用锁,就只能像 string 那样,不依赖自己实现的结点池,每次使用均向系统申请了。但如果加锁,由于 gamed 这个游戏逻辑引擎也会用到,到时候就有可能影响游戏逻辑的运行效率了。

但是,反观最近的修改,除了通过网络将 dbd 的数据打到 mongo 去,其余与最早的实现差别不多,字符串那个问题可以说是共享字符串引起,为什么 mapping 这个冲突问题以前没有暴露呢?我留意了一下原有的锁设置。原有的全局锁是一把 pthread 的读写锁,在接到 gamed 发过来的脏数据后,往脏数据队列里插入,这时候会加上写锁。如果写锁加上了,读锁自会处于等待状态。当写锁释放,读锁加上的时候,可以有多个读线程同时读。这时候,应该有可能出现读线程之间同时创建 mapping 的情况啊,为什么当时会没出现问题呢?待查

目前看来,只要在这些涉及到全局变量的点上,主要是 mapping 和字符串管理,加上可靠的锁保护,应该能够有效解决线程冲突的问题。只是,前提是能够重现线程冲突这个 bug