在(简要)回顾Go语言规范,有效Go和Go内存模型后,我仍然不清楚Go渠道如何在幕后工作.
他们是什么样的结构?它们的行为类似于线程安全的队列/数组.
他们的实现是否依赖于架构?
mna*_*mna 73
通道的源文件是(来自你的源代码根目录)/src/pkg/runtime/chan.go.
hchan是通道的中央数据结构,具有发送和接收链接列表(保存指向其goroutine和数据元素的指针)和closed标志.Lock在runtime2.go中定义了一个嵌入式结构,它可以作为互斥锁(futex)或信号量,具体取决于操作系统.锁定实现位于lock_futex.go(Linux/Dragonfly/Some BSD)或lock_sema.go(Windows/OSX/Plan9/Some BSD)中,基于构建标记.
通道操作都在这个chan.go文件中实现,因此你可以看到makechan,send和receive操作,以及select构造,close,len和cap内置函数.
有关频道内部运作的深入解释,你必须阅读Dmitry Vyukov自己的Go 类固醇通道(Go core dev,goroutines,scheduler and channels等).
Net*_*Kat 11
你问了两个问题:
Go 中的 Channel 确实“有点像线程安全的队列”,更准确地说,Go 中的 Channel 具有以下属性:
每次创建通道时,都会在堆上分配一个hchan结构,并返回一个指向 hchan 内存位置的指针,表示为通道,这就是 go 例程共享它的方式。
上述前两个属性的实现方式与带锁的队列类似。通道可以传递给不同 go 例程的元素被实现为循环队列(环形缓冲区),其索引位于 hchan 结构体中,索引说明元素在缓冲区中的位置。
循环队列:
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
Run Code Online (Sandbox Code Playgroud)
和指数:
sendx uint // send index
recvx uint // receive index
Run Code Online (Sandbox Code Playgroud)
每次 go 例程需要访问通道结构并修改其状态时,它都会持有锁,例如:将元素复制到缓冲区或从缓冲区复制元素、更新列表或索引。某些操作被优化为无锁,但这超出了本答案的范围。
go 通道的阻塞和非阻塞属性是通过使用两个保存阻塞 go 例程的队列(链表)来实现的
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
Run Code Online (Sandbox Code Playgroud)
每次 go-routine 想要将任务添加到满通道(缓冲区已满),或者从空通道(缓冲区为空)获取任务时,都会分配一个伪 go-routine sudog结构,并且 go-routine将 sudog 作为节点相应地添加到发送或接收等待者列表中。然后,go 例程使用特殊调用更新 go 运行时调度程序,这会提示何时应将它们退出执行 ( gopark) 或准备运行 ( goready)。请注意,这是一个非常简单的解释,隐藏了一些复杂性。
除了@mna已经解释过的特定于操作系统的锁实现之外,我不知道任何体系结构特定的约束优化或差异。
这是一个很好的演讲,大致描述了渠道的实现方式:https:
//youtu.be/KBZlN0izeiY
谈话说明:
GopherCon 2017:Kavya Joshi-了解频道
通道为goroutines提供了一种简单的通信机制,并为构建复杂的并发模式提供了强大的结构。我们将深入研究通道和通道操作的内部工作原理,包括运行时调度程序和内存管理系统如何支持它们。
| 归档时间: |
|
| 查看次数: |
9841 次 |
| 最近记录: |