Go 使用 channel 实现 CSP 模型。处理双方仅关注通道和数据本身,无需理会对方身份和数量,以此实现结构性解耦。在各文宣中都有 “Don't communicate by sharing memory, share memory by communicating.” 这类说法。但这并非鼓励我们不分场合,教条地使用 channel。
在我看来,channel 多数时候适用于结构层面,而非单个区域的数据处理。原话中 “communicate” 本就表明一种 “message-passing”,而非 “lock-free”。所以,它并非用来取代 mutex,各自有不同的使用场景。
有关 channel 实现方式,可参考《Go 学习笔记》第五版,下卷《源码剖析》。
从实现角度看,channel 算是一种很 “重” 的实现。在小粒度层面,其性能真算不得好。我们可用一个常见示例测试一下:用 channel 实现并发安全的计数器,或序号生成器。
![channel1](http://studygolang.qiniudn.com/160603/965399121b7701a70de79a69b0ffec74.jpg)
![channel2](http://studygolang.qiniudn.com/160603/91dc328f40594c49020e39c69f4205e8.jpg)
![channel3](http://studygolang.qiniudn.com/160603/470305fcadfef8b92f2bb5d98c9c5d36.jpg)
性能测试结果表明,差异远比想的要大得多。单就此例而言,还可以用原子操作(atomic)进一步优化。
![atomic1](http://studygolang.qiniudn.com/160603/bdf6374f8ff1a7f2c0848599d40a35b3.jpg)
![atomic2](http://studygolang.qiniudn.com/160603/663daf55ae53c939bf5ed0652d3a2f1c.jpg)
![atomic3](http://studygolang.qiniudn.com/160603/95c9abdc25c45a94718a984516be7e7a.jpg)
如果说 channel 适用于结构层面解耦,那么 mutex 则适合保护语句级别的数据安全。至于 atomic,虽然也可实现 lock-free 结构,但处理起来要复杂得多(比如 ABA 等问题),也未必就比 mutex 快很多。还有,sync.Mutex 本就没有使用内核实现,而是像 Futex 那样,直接在用户空间以 atomic 操作完成,因为 runtime 没有任何理由将剩余 CPU 时间片还给内核。
从没一种技术或技巧适用于所有场合。无论是表达,或者选型,都不应该脱离实际场景(上下文)。另外,就本系列的优化技巧而言,除非真有必要,否则大可不必理会这些 “奇技淫巧”。至于担心能否适应未来变化,我觉得多余。因为无论是架构、算法,亦或者是这些技巧,你本就应该有相应的机制确保在 “变化” 发生时第一时间获知。再者说,技巧并非照抄,无非多种思路而已。知其形,明其意,方为正理。
有疑问加站长微信联系(非本文作者)