Go的缓冲通道本质上是一个线程安全的FIFO队列。(请参阅是否可以将 Go 的缓冲通道用作线程安全队列?
我想知道它是如何实现的。它是否像中描述的那样无锁 是否有多个读取或写入线程的无锁队列之类的东西?
在 Go 的 src 目录 (grep -r Lock .|grep chan
) 中 greping 给出了以下输出:
./pkg/runtime/chan.c: Lock;
./pkg/runtime/chan_test.go: m.Lock()
./pkg/runtime/chan_test.go: m.Lock() // wait
./pkg/sync/cond.go: L Locker // held while observing or changing the condition
不过,不会锁定我的机器(MacOS,英特尔x86_64)。是否有任何官方资源可以验证这一点?
如果你在chan.c中阅读runtime·chansend
函数,你会看到在检查通道是否缓冲if(c->dataqsiz > 0)
之前调用了runtime·lock
。
换句话说,缓冲通道(以及一般的所有通道)使用锁。
您的搜索没有找到它的原因是您正在寻找带有大写字母 L 的"Lock"。用于通道的锁定函数是运行时中的非导出 C 函数。
您可以为喜欢的所有内容编写无锁(甚至无等待!)实现。像CMPXCHG这样的现代硬件基元足以普遍使用。但是编写和验证这些算法并不是最简单的任务之一。除此之外,可能存在更快的算法:无锁算法通常只是算法的一小部分。
据我所知,Dmitry Vyukov 过去曾为 Go 编写过一个无锁的 MPMC(多生产者/多消费者)通道实现,但由于 Go 的选择语句存在一些问题,该补丁被放弃了。有效地支持这一说法似乎真的很难。
然而,Go 通道类型的主要目标是提供一个易于用于各种问题的高级并发原语。即使不是并发编程专家的开发人员也应该能够编写正确的程序,这些程序可以在较大的软件项目中轻松查看和维护。如果您有兴趣挤出最后一点性能,则必须编写适合您需求的专用队列实现。