本文主要列出我曾犯錯的地方,多來自於面試題。
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