数据结构
字符串
// StringHeader is the runtime representation of a string.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
//
// Deprecated: Use unsafe.String or unsafe.StringData instead.
type StringHeader struct {
Data uintptr
Len int
}
- 不可变,不支持下标
- golang 的字符串是二机制安全的。
- 在和 byte 数组进行转换的时候如果 小于32B 则利用缓存,超过就会使用到堆空间
- 在同 range 关键字配合使用的时候实际是转换为了 rune 数组来进行的(utf-8 也是占用4个字节)。
数组
// An Array represents an array type.
type Array struct {
len int64
elem Type
}
- 数组是静态类型的数据结构,会占用一块连续的内存区域,不能动态的改变大小。
- 数组长度小于 4 的时候,运行时会被放入栈空间维护,大于 4 的时候位于内存的静态只读区域。
- 数组作为参数传递的时候是按值传递的,也就是说数组会拷贝出一份来传入函数中。
切片
// SliceHeader is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
//
// Deprecated: Use unsafe.Slice or unsafe.SliceData instead.
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
- 切片是动态的类型数据,设计思想基于共享内存的方式来进行的。
- 使用切片的时候要注意 append 时候可能导致切片地址变更,而截取操作会共享一个切片内存空间。
- 切片扩容时如果大小小于 256 会进行翻倍,如果超过,每次增加 1.25 倍。
- 在分配的时候 如果大小超过 64 KB 会直接在堆里进行分配。
- 传参的过程中不会导致底层的数据全部复制。
- 同数组的比较上来看,就是减少了复制次数、重用了内存空间、可以动态的改变大小。
- cap函数可以利用在切片上查看容量
- 切片如果使用范围查找,比如[:i+1] 时,会判断是否超过长度,不会产生溢出
Map
// A header for a Go map.
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
- map 的结构由哈希表、哈希桶、溢出桶组成,是一个KV结构的数据。
- 支持随机的读写。
- 扩容的时候可能由于负载因子超过6.5(默认)或者溢出桶数据太多导致。每次扩容翻倍,采用写时复制的技术来减少不可用时间。
- 解决冲突的策略是开放寻址法。
- 配合range 的时候,每次遍历都是无序的(源码中进行了随机化)。
- 根据hash 后 N 位进行哈希桶定位,N = log2(桶大小)
- 并发不安全,设计者认为 map 主要用在局部操作,所以也没提供。
- 扩容条件:
- 超过负载因子,扩容 2 倍
- 溢出桶数量超过 2^15 时,等量扩容(map 里元素总数少,但是 bucket 数量多,比如插入后删除了很多,所以重新开辟 bucket 空间,进行整理,减少内部碎片)
map 的寻址
https://www.jb51.net/article/273673.htm
Channel
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}
type waitq struct {
first *sudog
last *sudog
}
- channel 是 golang 对 CSP(communicating sequential process,通信顺序进程) 的具体实现,即通过通道进行通信实现任务的同步。
- channel 是 goalng 提供的同步工具的核心数据结构之一。是并发安全的。
- 底层由数组构成的 buf 和 双向链表组成的读写等待队列组成。
- 大小在一开始就会被确定(数组管理缓冲区域),无法动态修改。属于静态类型
- 使用中,channel 可能是 nil、无缓冲、带缓冲三种情况。
- nil 对读写都会阻塞
- 无缓冲的和有缓冲的主要区别在无缓冲chnnel 如果在没有读等待的时候写会被阻塞
- 有缓冲的只有在没有缓冲空间的时候才会写阻塞。
- channel 同样提供了只读、只写、读写channel 类型。
- close 后依然可以读取channel 的数据,如果关闭的channel 中没有数据了,会返回对应的数据的 0 值。
- select 多个channel 的时候会对channel 进行排序,防止死锁
- select 的时候,会对case的顺序用洗牌算法打乱,防止饥饿
Context
- golang 的 context 的设计是作为调用链路的信息载体存在的。
- Backgroud 会返回一个空的 ctx
- WithCancel 返回一个带通道的 ctx,可以通过调用cancel 来向通道发送一个信号,需要监听 ctx->done channel。
- WithTimeOut 超时的 ctx,同时会返回一个 cancel 函数,帮助显示的取消 context。子协程同样需要监听 ctx->done 这个channel。
- WithValue 方法可以返回一个带 KV 的ctx,用来传递自定义的一些数据,比如 logID 等等。