golang slice扩容原理
- 新申请容量 cap,如果大于 2 倍旧容量 (oldcap),要扩容的容量(newcap)= 新申请容量 cap
- 如果旧容量(oldcap)< 1024, 要扩容的容量(newcap)= 旧容量(oldcap)* 2
- 如果旧容量(oldcap) >=1024,要扩容的容量(newcap)= 旧容量(oldcap)* 1.25
1.18版本以后新逻辑
- 新申请容量 cap,如果大于 2 倍旧容量 (oldcap),要扩容的容量(newcap)= 新申请容量 cap
- 如果旧容量(oldcap)< 256, 要扩容的容量(newcap)= 旧容量(oldcap)* 2
- 如果旧容量(oldcap) >=256,每次增加 (newcap + 3 * 256) / 4
new和make的区别
- new针对的是值类型,比如int,string,array,make针对的是引用类型,比如map,slice,channel
- new返回变量的指针,make返回变量本身
- new分配的空间被清零,make分配空间后会进行初始化
数组和切片的区别
- 数组的长度固定,只能存储同类型的数据结构,切片的长度是动态的,可以动态扩容
- 数组是值类型,切片是引用类型,切片底层是一个结构体
defer的执行顺序
defer是先进后出
/* 结果
defer1: 1
defer2: 2
return: 2
*/
package main
import "fmt"
func b() (i int) {
defer func() {
i++
fmt.Println("defer2:", i)
}()
defer func() {
i++
fmt.Println("defer1:", i)
}()
return i //或者直接写成return
}
func main() {
fmt.Println("return:", b())
}
select使用场景
select会结合case语句选择一个通道进行执行,select实现一种监听模式,用在无限循环中。
map是否线程安全,如何实现并发安全
map不是线程安全的且是无序的,可以使用sync.Map
channel是线程安全吗
channel底层是个hchan的结构体,使用了mutex锁保证安全,有循环发送队列和接收队列
https://dave.cheney.net/2013/04/30/curious-channels
GPM模型
G代表goroutine,P代表逻辑处理器,M代表系统级线程。
https://blog.tianfeiyu.com/2021/12/12/golang_gpm/
为什么协程比线程更加轻量
因为协程是调度器实现的,是用户级线程,是由调度器调度,无需进行上下文切换。