本文主要列出我曾犯错的地方,多来自于面试题。
defer
下列输出结果:
package main
func main() {
i := 0
defer func() {
fmt.Println("func print:" + strconv.FormatInt(int64(i), 10))
}()
defer fmt.Println("first print:" + strconv.FormatInt(int64(i), 10))
for ; i < 3; i++ {
defer fmt.Println("second print:" + strconv.FormatInt(int64(i), 10))
}
defer fmt.Println("end print:" + strconv.FormatInt(int64(i), 10))
i = 100
}
defer 后面语句的运行,如果是语句,那么其中变量的值是编译过程就会确定的,如果是闭包或者函数,则是在运行过程才能确定的。
输出结果:
end print:3
second print:2
second print:1
second print:0
first print:0
func print:100
数组,cap,len
cap([]interface{}) int 是数组实际的容量
len([]interface{}) int 是数组包含的元素个数
package main
func main() {
i1 := make([]int, 3)
fmt.Printf("i1 length is %d\n", len(i1)) // 3
fmt.Printf("i1 cap is %d\n", cap(i1)) // 3
fmt.Printf("i1 is %d\n", i1) // [0, 0, 0]
i2 := make([]int, 3, 5)
fmt.Printf("i2 length is %d\n", len(i2)) // 3
fmt.Printf("i2 cap is %d\n", cap(i2)) // 5
fmt.Printf("i2 is %d\n", i2) // [0, 0, 0]
i3 := i2[1:]
fmt.Printf("i3 length is %d\n", len(i3)) // 2
fmt.Printf("i3 cap is %d\n", cap(i3)) // 4
fmt.Printf("i3 is %d\n", i3) // [0, 0]
}
GC
go语言的gc机制,只要指针发生引用,即使是局部变量,也不会被GC。
下列代码输出:
package main
var s map[string]*Student
type Student struct {
Name string
Age int
}
func Test() {
i := make([]*Student, 3)
i[0] = &Student{
Name: "zhang",
Age: 20,
}
i[1] = &Student{
Name: "li",
Age: 21,
}
i[2] = &Student{
Name: "wang",
Age: 19,
}
s = make(map[string]*Student, 3)
for _, v := range i {
s[v.Name] = v
}
}
func main() {
Test()
for _, v := range s {
fmt.Println(fmt.Sprintf("the value is : %v", v))
}
}
输出结果:
the value is : &{zhang 20}
the value is : &{li 21}
the value is : &{wang 19}
channel
channel是go语言用于Goroutine通信的,用通信来共享内存,你可以声明各种类型的chan,可以是int,string,也可以是个函数指针。
chan的读和写通过符号 <- 来配合实现,例如:读 (i <- chan),写 (chan <- 1)。
chan的特性:读(写)chan之前,必须保证有Goroutine做写(读)的操作,如果读写语句都在同一Goroutine之内,就会发生死锁。
- 例如:死锁写法
package main
func main() {
ch1 := make(chan int)
fmt.Println("start")
ch1 <- 1
fmt.Println("write ch1")
if <-ch1 == 1 {
fmt.Println("ch1 is 1")
}
fmt.Println("end")
}
输出结果:
start
chan写入以后,main就发生死锁了,因为没有别的Goroutine读。
解决办法一:将后续的读操作用闭包或者函数的形式,在chan写操作之前启动Goroutine
package main
func main() {
ch1 := make(chan int)
fmt.Println("start")
go func() {
fmt.Println("read ch1")
i := <-ch1
if i == 1 {
fmt.Println("ch1 is 1")
}
fmt.Println("goruntine end")
}()
fmt.Println("write ch1")
ch1 <- 1
fmt.Println("end")
}
输出结果:
start
write ch1
read ch1
ch1 is 1
goruntine end
end
解决办法二:将chan提前声明容量,然后再写入,后续即使在同一个线程里读写,只要保证先写后读,就不会发生死锁
package main
func main() {
ch1 := make(chan int, 1)
fmt.Println("write ch1")
ch1 <- 1
fmt.Println("read ch1")
if <-ch1 == 1 {
fmt.Println("ch1 is 1")
}
fmt.Println("end")
}
输出结果:
write ch1
read ch1
ch1 is 1
end
解决方法三:使用select语句,select是专门为chan设计的,case条件必须是对chan的操作,如果所有case都没法满足,则会进入default。
package main
func main() {
ch1 := make(chan int, 1)
fmt.Println("write ch1")
ch1 <- 1
fmt.Println("select ch1")
select {
case i := <-ch1:
fmt.Println(fmt.Sprintf("ch1 is %d", i))
case ch1 <- 0:
fmt.Println("write ch1 0")
default:
fmt.Println("all faild")
}
fmt.Println("end")
}
输出结果:
write ch1
select ch1
ch1 is 1
end
即使没有写入操作,也不会发生死锁,至于原因我在了解之后再细说吧。
package main
func main() {
ch1 := make(chan int, 1)
fmt.Println("select ch1")
select {
case i := <-ch1:
fmt.Println(fmt.Sprintf("ch1 is %d", i))
case ch1 <- 0:
fmt.Println("write ch1 0")
default:
fmt.Println("all faild")
}
fmt.Println("end")
}
输出结果:
select ch1
write ch1 0
end