数据结构

Go interface

interface 底层结构 #

interface{} 底层存储结构如下所示:

// ----------- runtime/runtime2.go -----------------

type iface struct {
	tab  *itab
	data unsafe.Pointer
}

type eface struct {
	_type *_type
	data  unsafe.Pointer
}

type itab = abi.ITab
type _type = abi.Type

// ----------- internal/abi/iface.go -----------------

// The first word of every non-empty interface type contains an *ITab.
// It records the underlying concrete type (Type), the interface type it
// is implementing (Inter), and some ancillary information.
//
// allocated in non-garbage-collected memory
type ITab struct {
	Inter *InterfaceType
	Type  *Type
	Hash  uint32     // copy of Type.Hash. Used for type switches.
	Fun   [1]uintptr // variable sized. fun[0]==0 means Type does not implement Inter.
}

可以看到 interface{} 底层被存储为两种类型:

...

Go channel

channel 数据结构 #

channel 被用来实现 goroutine 间的通信,以通信的方式共享内存。

如下代码使用 make 函数会在 堆上 分配一个 runtime.hchan 类型的数据结构并初始化,ch 是存在于 f 函数栈帧上的一个指针,指向堆上的 hchan 数据结构。

func f() {
    ch := make(chan int)
    ...
}

channel 分为 无缓冲 和 有缓冲 两种。对于有缓冲的 channel 来说,使用循环数组 buf 来存储数据。

Golang 图解channel之数据结构

select 和 channel 机制 #

通过 gouroutinechannel 实现并发中基于通信的内存同步, channel 还可以和 selectcanceltimeout 结合使用

channel 分为不带缓冲和带缓冲的,不带缓冲的可以认为是“同步模式”,带缓冲的可以认为是“异步模式”。

channel 的数据结构如下,包含一个 循环数组 buf 存储缓存中的数据 和当前读写在数组中的索引,以及缓存大小,还有一个阻塞

type hchan struct {
	qcount   uint           // chan 中元素数量, 对于无缓冲的为 0, 数据直接从发送方传递给接收方
	dataqsiz uint           // chan 底层循环数组的大小, 对于无缓冲的为 0
	buf      unsafe.Pointer // 指向底层循环数组的指针, 只针对有缓冲的 channel
	elemsize uint16 // chan 中元素大小
	closed   uint32 // chan 是否关闭的标志
	timer    *timer // timer feeding this chan
	elemtype *_type // chan 中元素类型
	sendx    uint   // 下一个要发送元素在循环数组中的索引
	recvx    uint   // 下一个要接收元素在循环数组中的索引
	recvq    waitq  // 阻塞的尝试从此 channel 接收数据的 goroutine, sudog 双向链表
	sendq    waitq  // 阻塞的尝试向此 channel 发送数据的 goroutine, sudog 双向链表
	lock mutex      // 保护 hchan 所有字段的锁
}

type waitq struct {
	first *sudog
	last  *sudog
}

阻塞模式和非阻塞模式:由 select 是否包含 default 确定

...