golang 學習筆記 - defer/數組/GC/channel

本文主要列出我曾犯錯的地方,多來自於面試題。

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之內,就會發生死鎖。

  1. 例如:死鎖寫法
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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章