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/

为什么协程比线程更加轻量

因为协程是调度器实现的,是用户级线程,是由调度器调度,无需进行上下文切换。