chan
从channel读数据
<span style="font-size:inherit;">如果缓冲区中有数据,则从缓冲区取出数据,结束读取过程</span><br>
如果缓冲区没数据,且等待发送队列sendq不为空,则从sendq中取出G,把G中数据读出,最后把G唤醒,结束读取过程
如果缓冲区没数据,且等待发送队列sendq为空,则将当前goroutine放入等待接收队列recvq,进入睡眠,等待被唤醒
向channel写数据
如果缓冲区有空余位置,则将数据写入缓冲区,结束写入过程
如果缓冲区没有空余位置,且等待接收队列recvq为空,则将当前goroutine放入sendq,进入睡眠,等待被唤醒
如果缓冲区没有空余位置,且等待接收队列recvq不为空,则从recvq取出G,将数据写入,最后唤醒G,结束写入过程
关闭channel
关闭channel时,将revcq中G全部唤醒,本该写入G的数据位置为nil,将sendq中G全部唤醒,但这些G会panic
panic情况
关闭值为nil的channel
关闭已经关闭的channel
向已关闭的channel写数据
注意
select的case读取channel的时候不会阻塞,这是由于case语句编译后读取channel会明确传入不阻塞参数,此时读不到数据不会将当前goroutine放入recvq而是直接返回
遍历channel时,若写数据的goroutine退出,则系统panic
slice
创建slice
cap为slice首元素到底层数组尾元素的长度
slice扩容
如果原slice容量小于1024,则容量扩大为原slice的2倍
如果原slice容量大于等于1024,则容量扩大为原slice的1.25倍
copy
切片copy,拷贝数量取两个切片长度最小值,不会发生扩容
map
bucket哈希桶
哈希冲突
当两个或以上数量的键哈希到同一bucket时,我们称之为哈希冲突
Go使用链地址法解决键冲突,由于每个bucket可以存放8个键值对,超过8个时,创建一个bucket,使用类似链表的方式将新bucket连起来,新bucket被称为overflow bucket
负载因子
键数量/bucket数量
负载因子过小,说明空间利用率低
负载因子过大,说明哈希冲突严重
扩容
扩容条件
负载因子大于6.5
overflow数量大于2^15
增量扩容
当负载因子过大时,就新建一个buckets,长度为原来的两倍,然后将旧bucket数据逐步搬迁到新bucket,每次访问map搬迁两个key
等量扩容
buckets数量不变,重新做一遍类似搬迁的动作,把松散的键值对重新排列一次,是bucket效率更高
查找
根据key计算哈希值
取哈希值低位与hmap.B取模,确定bucket位置
取哈希值高位在tophash数组中查询,若tophash[i]与哈希值相等,则去找到该bucket中key值进行比较,若bucket中没有该key,则依次查找overflow
如果当前处于搬迁状态,则优先从oldbuckets中查找
插入
查找key是否已经存在,若存在则直接更新,不存在则插入
iota
iota表示const代码块的行索引
const常量的特点:第一行必须有表达式,后续如果没有表达式则继承上一行的表达式
string
string可以为空(长度为0),但不能是nil
string对象不可以修改
string与[]byte的相互转换需要进行内存拷贝
[]byte临时转换为string不需要内存拷贝