Go底层原理笔记 | 青训营笔记

95 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

Go基础之基础:

  1. 值接收和指针接收区别?值接受改变的是对象的副本,指针接收改变的是对象本身。
  2. go 是如何实现继承的? 通过结构体组合来实现继承。
  3. new和make的区别? 不同点:make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

Slice(线程不安全)

  1. slice底层就是一个数组,数组固定大小,slice大小不固定。
  2. 深拷贝(创建新的内存地址存储数据,拷贝的是数据本身)copy函数
  3. 浅拷贝(拷贝的是数据的地址,拷贝前后内存地址不变)复制函数
  4. Slice 扩容机制:原Slice容量小于1024,扩大两倍。原Slice容量大于1024,扩大1.25倍。(最新版本忘了)
  5. Slice是线程安全嘛???不是滴,底层结构就是个数组,没有加锁等操作,不支持并发读写,所以不是安全的。
  6. go实现线程安全的方法:互斥锁/读写锁/sync.once/channle

Map(线程不安全)

  1. Map为什么是无序遍历?因为开始遍历时,是随机一个bucket开始遍历的,再从bucket的cell随机找个数据遍历。
  2. Map线程安全嘛???怎么解决捏???肯定不安全啊,1.使用读写锁sync.RWMutex;2.go自带的sync.Map
  3. Go map和sync.Map谁的性能好,为什么?(有点问题回头再看)sync.Map适合读多写少的场景,map+读写锁适合写多读少的场景。
  4. 底层原理:使用hmap作为底层,每个hamp可以拥有多个bucket,每个bucket可以存放8个键值对,发生哈希冲突,go使用链地址法解决冲突。通过哈希值得到低八位算出key在哪个桶,在通过高8位算出key在桶的具体位置。
  5. 扩容条件:负载因子太小,溢出桶的数量多了(解决:等量扩容,buckets数量不变,只是重做一次增量扩容的搬迁动作,将松散的键值对重新排列一番。)。负载因子太大,冲突严重(解决:增量扩容,go采取逐步搬迁,每次访问map搬迁两个键值对。)

Channel(线程两种类型:安全)

  1. 两种类型:无缓冲/有缓冲
  2. 三种模式:写操作/读操作/双向读写
  3. 触发panic的条件:关闭nil的channel;关闭已关闭的channel;向关闭的channel写数据
  4. 如何实现线程安全?hchan结构采用Mutex锁来保证数据读写安全,对循环数组buf入队出队时,必须先获取互斥锁。
  5. CSP模型:有点像消息队列,生产者和消费者不用考虑其他,只需要往channel读写数据就好了。(推崇通过通信来共享内存)缺点:容易发生死锁。
  6. 底层:环形队列,G等待队列,类型信息和锁构成。
  7. 怎么判断channle为空?可以用 for range读取来查看通道是否关闭。

实战项目

猜数字(记住随机数种子就行)/在线字典(用api接口就行)/Socks5 代理(有点老,不写了)

总结

golang语法相对来说简单易学,多看看每种数据类型的底层结构,加深理解,主要是slice/map/channle,指针只是简单用法,相比C/C++的指针简单多了。