前言:语言特性没必要一下学完,而应该学习常用的,剩下的用的时候再查就行,语言类的书的定位应该是工具书,而不是学习的书。
go的map实现?
将键映射到存储桶,链来解决冲突。需要使用sync.map或加锁控制互斥,动态扩容。
怎么判断hashmap是否该扩容?键值对数量(是当前map中存储的键值对的数量)/存储桶数组长度(是当前map底层数组的长度)= 负载因子,负载因子超过一定的阈值时,就会触发map的扩容操作,默认为6.5。
不扩容,链就会很长,扩容的话桶会变多 ,hash算法也会改变,导致之前一个桶的会分到两个桶中去,采用增量式扩容的方式,通过逐步复制原有存储桶数组的内容到新的存储桶数组中来实现扩容,将复制操作分摊到多次操作中,以减少单次扩容的开销。
go的gmp模型?
GMP模型的设计目标是在多核处理器上实现高效的并发和并行操作。具体内容参考brpc那篇博客。
G 协程
M 线程
P 逻辑处理器
golang与c类似,静态,但有GC。
:=定义变量 或 var a int。
defer语句会延迟到外层函数返回后再执行。
切片:前包含后不包含,可用make创建切片。
栈逃逸:任何时候如果一个函数的栈帧上的值在函数外共享,这个值将会在分配在堆上。
闭包特性:
func fa(a int) func(i int) int{
return func(i int) int{
println(&a,a)
a=a+i
return a
}
}
f:=fa(1),g:=fa(1),f(1) 为1 ,f(1) 为2,g(1) 为1 ,g(1) 为2。
map[K]V
for k,v:=range map{
}
发生panic后,会逐层向上返回,所以一般把recover写在defer中。
接口:
type A interface{
Print()
}
然后定义一个struct 实现它的Print()函数,就可以var i A=struct实例化的对象,i.Print()。
goroutine,协程并行,可以先定义一个函数func A(),然后在main中go A(),main函数本身就是一个goroutine。
goroutine间用chan(通道)通信,使用fifo先进先出。
make(chan datatype),无缓冲,一般用来同步goroutine
1: c <- struct{}{} (此时1会阻塞至2执行)
2:<-c;size int 即用户指定的channel缓冲区大小,不指定则为0。由于发送和接收操作都是阻塞式的,所以这个程序会一直等待直到发送和接收操作匹配,才能正常结束。
make(chan datatype,10)有缓冲,(chan满了再放会阻塞,空的去拿会阻塞)。
管道的关闭:使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是仍然可以从该channel读取数据。
同步:
var wg sync.WaitGroup
wg.Add(1)
defer wg.Done()//会 -1
wg.Wait()
select可监听多个chan,select是Golang在语言层面提供的多路IO复用的机制
for {
select{
case ch<-0:
处理1
case ch<-1:
处理2
default:
处理3
} // 当一个都不满足时会走default,无default会阻塞,0、1的写入是随机的。
}
反射:
s:=Student{}
rt:=reflect.Typeof(s) ,传Stuent的结构体也可以,不是只能传实例化的对象。
rt.FieldByname("Name")
依赖注入和控制反转
inj := inject.New()
inj.Map("tom") // 注入 tom的生命不是我控制了
inj.Invoke(函数名) // 这个函数有个参数是string
goroutine中使用recover,解决协程中出现panic,导致程序崩溃问题。
func test() {
// 这里使用defer + recover
defer func() {
// 捕获test的panic
if err := recover(); err != nil {
fmt.Println("test() 发生错误 ", err)
}
}()
var myMap map[int]string
myMap[0] = "golang" // error
}
select本身不带循环,需要外层的for,default通常不用,会产生忙轮询 写个sleep感觉可以
for {
select {
case <-chan1:
// ......
case chan2<-1:
// ......
default:
//都没成功进入......
}
}
有时候会出现协程阻塞的情况,那么我们如何避免这个情况?可以使用select来设置超时
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v:= <-c:
fmt.Println(v)
// 5秒钟自动关闭,避免长时间超时
case < - time.After(5 * time.Second):
fmt.Println("timeout")
o<-true
break
}
}
}()
// 有值就主协程走,主协程走完就都没了
<-o
fmt.Println("程序结束")
}
golang
于 2022-04-01 17:18:15 首次发布
![](https://img-home.csdnimg.cn/images/20240711042549.png)