闭包的理解
变量是存在闭包当中,下一次调用的时候,还是会用这个变量的值
当函数func2()
有变量值的时候,函数的调用时候的变量值是存在于调用的函数栈当中,也就是val值存在printFunc2()
函数栈中,所以defer
函数对于val是有影响的。
如果函数没有变量值,val仅限于func3()
中。
Goroutine 下面的代码会出现重复打印多次相同的元素。
出现的原因就是在goroutine栈中使用变量的时候,gorutine栈中的变量还没有刷新
1 2 3 4 5 6 7 8 9 10 11 12 func main () { data := make (map [int ]int , 10 ) for i := 1 ; i <= 10 ; i++ { data[i] = i } for key, value := range data { go func () { fmt.Println("k->" , key, "v->" , value) }() } time.Sleep(time.Second * 5 ) }
解决办法 ,传入值,强制刷新变量值。
1 2 3 4 5 6 7 8 9 10 11 12 func main () { data := make (map [int ]int , 10 ) for i := 1 ; i <= 10 ; i++ { data[i] = i } for key, value := range data { go func (key,value int ) { fmt.Println("k->" , key, "v->" , value) }(key,value) } time.Sleep(time.Second * 5 ) }
Goroutine栈
go-v1(最小栈空间4kB)
go-v1.2(最小栈空间8kB)
go-v1.3(分段栈替换为连续栈)
go-v1.4(最小栈空间2kB)
分段栈的Hot split问题: 栈空间的地址是不连续的,它们之间采用指针的方式把他们串联起来。
在之前扩缩容算法:栈空间满了立即触发扩容,不满立即触发缩容。当我们调用一个函数的时候,相应的需要一个栈的扩容,调用完这个函数之后,就会对这个栈进行缩容,如果用for循环调用一个函数,就会频繁的扩缩容,这个过程会产生一个巨大的额外开销,对性能影响比较大
在1.3以前栈有一个扩容的现象的时候,内存地址是不会变化的;1.3及以后的连续栈内存地址会发生变化,主要是复制到一个新分配的栈空间中去,
defer defer原理就是一个栈,同时出现两个defer会先运行后出现的defer
1 2 3 4 5 6 func main () { m := 10 defer fmt.Printf("first defer %d\n" ,m) m = 100 defer fmt.Printf("second defer %d\n" ,m) }
输出结果自然先second后first
结合闭包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func main () { m := 10 defer fmt.Printf("first defer %d\n" , m) m = 100 defer func () { fmt.Printf("second defer %d\n" , m) }() m *= 10 defer fmt.Printf("third defer %d\n" , m) funcVal := func1() funcVal() } func func1 () func () { fmt.Println("before return" ) return func () { defer fmt.Println("in the return" ) } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func main () { defer fmt.Printf("main %d\n" , func2()) } func func2 () int { sumA := 100 sumB := 100 sum := sumA + sumB defer func () { fmt.Printf("func2 first %d\n" , sum) }() defer fmt.Printf("func2 second %d\n" , sum) return sum * 10 }
sum*10只能作用与main函数中。所以结果变化为
slice扩容 少于1024双倍扩容,大于1024是1.25倍扩容
1 2 3 4 5 6 7 func main () { var arr1 []int64 for i := 0 ; i < 512 ; i++ { arr1 = append (arr1, int64 (i)) } fmt.Printf("len=%d,cap=%d\n" , len (arr1), cap (arr1)) }
1 2 3 4 5 6 7 8 func main () { var arr1 []int64 for i := 0 ; i < 513 ; i++ { arr1 = append (arr1, int64 (i)) } fmt.Printf("len=%d,cap=%d\n" , len (arr1), cap (arr1)) }
1 2 3 4 5 6 7 8 func main () { var arr1 []int64 for i := 0 ; i < 1025 ; i++ { arr1 = append (arr1, int64 (i)) } fmt.Printf("len=%d,cap=%d\n" , len (arr1), cap (arr1)) }
批量append和单个append 查表(sizeclasses.go):比这个大最接近的数
1 2 3 4 5 6 7 8 9 10 11 func main () { var arr1 []int32 for i := 0 ; i < 1025 ; i++ { arr1 = append (arr1, int32 (i)) } fmt.Printf("len=%d,cap=%d\n" , len (arr1), cap (arr1)) }
1 2 3 4 5 6 7 8 9 10 11 12 func main () { var arr1 []int64 var arr2 []int64 for i := 0 ; i < 1025 ; i++ { arr1 = append (arr1, int64 (i)) } fmt.Printf("len=%d,cap=%d\n" , len (arr1), cap (arr1)) arr2 = append (arr2, arr1...) fmt.Printf("len=%d,cap=%d\n" , len (arr2), cap (arr2)) }
nil是否一定相等? 非空接口iface,空接口eface。它们除了data都还有一个变量,_type表示data的数据类型
1 2 3 4 5 6 7 8 9 10 11 func main () { var x *int = nil var y interface {} = x fmt.Println(x == y) fmt.Println(x == nil ) fmt.Println(y == nil ) var z interface {} = nil fmt.Println(z == nil ) fmt.Println(y == z) }
并发 Mutex,互斥锁、不可重入锁;在同一个goroutine下,重复上锁的话,就会发生panic
defer是在return之后运行
下面代码会panic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 var mu sync.Mutexvar chain string func main () { chain = "main" A() fmt.Println(chain) } func A () { mu.Lock() defer mu.Unlock() char = char + "--> A" B() } func B () { char = char + "--> B" C() } func C () { mu.Lock() defer mu.Unlock() char = char + "--> C" }
正确应该为:
1 2 3 4 5 6 7 8 9 10 11 12 var mu sysnc.Mutexfunc main () { mu.Lock() go func () { mu.Lock() fmt.Println("I'm OK" ) defer mu.Unlock() }() mu.Unlock() time.Sleep(time.second) return }
如何调用panic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func proc () { panic ("ok" ) } func main () { go func () { t := time.NewTicker(time.Second) for { select { case <-t.C: go func () { defer func () { if err := recover (); err != nil { fmt.Println(err) } }() fmt.Println(time.Now()) proc() }() } } }() select {} }
协程顺序执行问题 使用三个协程,每秒打印cat、dog、fish,顺序不能变化(协程1打印cat、协程2打印dog、协程3打印fish)
如果取值和放入值对调,会产生死锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package mainimport ( "fmt" "sync" "time" ) func cat (fishCH, catCH chan struct {}, wg *sync.WaitGroup) { wg.Add(1 ) go func () { for { fmt.Println("cat" ) catCH <- struct {}{} <-fishCH } wg.Done() }() } func dog (catCH, dogCH chan struct {}, wg *sync.WaitGroup) { wg.Add(1 ) go func () { for { <-catCH fmt.Println("dog" ) dogCH <- struct {}{} } wg.Done() }() } func fish (dogCH, fishCH chan struct {}, wg *sync.WaitGroup) { wg.Add(1 ) go func () { for { <-dogCH fmt.Println("fish" ) time.Sleep(time.Second) fishCH <- struct {}{} } wg.Done() }() } func main () { catCH := make (chan struct {}) dogCH := make (chan struct {}) fishCH := make (chan struct {}) wg := sync.WaitGroup{} cat(fishCH, catCH, &wg) dog(catCH, dogCH, &wg) fish(dogCH, fishCH, &wg) wg.Wait() }
tag原理 反射的原理
功能很强大,但是会消耗性能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package mainimport ( "fmt" "reflect" ) type UserInfo struct { Name string `bilibili:"BILIBILI_NAME"` PublicWX string `bilibili:"BILIBILIB_PUBLICWX"` } func PrintTag (prt interface {}) { reType := reflect.TypeOf(prt) if reType.Kind() != reflect.Ptr || reType.Elem().Kind() != reflect.Struct { panic ("传入的参数不是结构体和指针" ) return } v := reflect.ValueOf(prt).Elem() fmt.Println("v:" , v) for i := 0 ; i < v.NumField(); i++ { field := v.Type().Field(i) tag := field.Tag labelTag := tag.Get("bilibili" ) fmt.Println("" , labelTag) } } func main () { usrInfo := &UserInfo{ Name: "原生驿站" , PublicWX: "publicWX" , } PrintTag(usrInfo) }
为什么需要response.Body.Close() 如果在解析Body的时候中断了,Body就没有办法读到EOF
信号,也就意味着Body不会被清空掉,http请求就浪费了。
线程回收的问题 Golang不会回收线程,这个问题官方还没有解决(2022.1.9)
强制回收线程:将gorouting强制绑定在线程上面,gorouting在退出的时候没有解锁这个线程,这个线程就会被操作系统终止
指针 下面代码的输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport "fmt" func main () { var a int = 1 var b *int = &a var c **int = &b var x int = *b fmt.Println("a = " , a) fmt.Println("&a = " , &a) fmt.Println("*&a = " , *&a) fmt.Println("b = " , b) fmt.Println("&b = " , &b) fmt.Println("*&b = " , *&b) fmt.Println("*b = " , *b) fmt.Println("c = " , c) fmt.Println("*c = " , *c) fmt.Println("&c = " , &c) fmt.Println("*&c = " , *&c) fmt.Println("**c = " , **c) fmt.Println("***&*&*&*&c = " , ***&*&*&*&*&c) fmt.Println("x = " , x) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 a = 1 &a = 0xc0000aa058 *&a = 1 b = 0xc0000aa058 &b = 0xc0000ce018 *&b = 0xc0000aa058 *b = 1 c = 0xc0000ce018 *c = 0xc0000aa058 &c = 0xc0000ce020 *&c = 0xc0000ce018 **c = 1 ***&*&*&*&c = 1 x = 1