Go語言基礎
規則
大寫字母開頭變量爲共有變量,小寫字母開頭爲私有變量(函數類似)
Iota
iota枚舉(默認開始值爲0,const中每增加一行,值加1)
slice
引用類型(類似於動態數組),相比於array,它沒有長度限制
make & new
- make返回初始化後的值(非零),只能被用於slice、map、channel
- new返回指針,一般很少被使用
goto(無條件跳轉,配合標籤使用,無條件跳轉至標籤,可用於跳出循環)
由於可隨意跳轉,在龐大的系統中不利於debug,所以在coding中會通過結構化程序設計來規避這類問題,那麼,在go語言設計者爲何會繼續沿用goto呢?
- 最開始提出goto有害論主要是當時的背景導致的,最開始使用的是非結構化的過程語言,例如BASIC和各種彙編語言
- 可讀性強,可以書寫出乾淨的退出功能(將所有退出方式統一編輯,利用goto進行跳轉)
- 低級性能改進,goto語句執行的非常快
defer(延遲語句,函數執行到最後會按照逆序執行defer語句,可用於處理資源泄漏問題)
- 在函數中使用時,採用先進後出模式
panic & recover(go沒有異常機制,使用這兩個內置函數來代替異常機制)
- 調用panic中斷函數執行(但defer語句仍然會被執行),退出執行
- 利用recover + defer可以捕獲panic的輸入值,並恢復正常執行
package main
import "fmt"
func test_goto() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
for sum := 0; sum < 15; sum++ {
if sum == 12 {
panic("12 is error")
}
fmt.Println(sum)
}
}
func main() {
test_goto()
fmt.Println("over!")
}
輸出結果爲:
0
1
2
3
4
5
6
7
8
9
10
11
12 is error
over!
使用指針作爲receiver
如果不使用指針作爲receiver,那麼傳遞的是參數的copy,如果修改值,只會作用在copy上
interface
GoLang本身是不支持泛型(通用類型)編程的,這也是它被詬病最多的地方,爲了彌補這一缺陷,可以通過interface來實現泛型編程(PS:go只是暫時不支持,因爲這會增加設計的複雜度,以後可能會支持)
反射
Go利用reflect實現反射(即檢查程序在運行時的狀態),要去反射是一個類型的值 (這些值都實現了空 interface),首先需要把它轉化成 reflect 對象 (reflect.Type 或者 reflect.Value,根據不同的情況調用不同的函數)。
要反射的字段必須是可讀寫的字段,否則會報錯。
併發
goroutine
goroutine 是 Go 並行設計的核心。goroutine 說到底其實就是協程,但是它比線程更小,十幾個 goroutine 可能體現在底層就是五六個線程,Go 語言內部幫你實現了這些 goroutine 之間的內存共享。執行 goroutine 只需極少的棧內存 (大概是 4~5 KB),當然會根據相應的數據伸縮。也正因爲如此,可同時運行成千上萬個併發任務。goroutine 比 thread 更易用、更高效、更輕便。
設計上我們要遵循:不要通過共享來通信,而要通過通信來共享。
但在 Go 1.5 以前調度器僅使用單線程,也就是說只實現了併發。想要發揮多核處理器的並行,需要在我們的程序中顯式調用 runtime.GOMAXPROCS (n) 告訴調度器同時使用多個線程。GOMAXPROCS 設置了同時運行邏輯代碼的系統線程的最大數量,並返回之前的設置。如果 n < 1,不會改變當前設置。
channel
channel類型的值可以發送接收值,注意,必須使用 make 創建 channel。默認情況下,channel 接收和發送數據都是阻塞的,除非另一端已經準備好,這樣就使得 Goroutines 同步變的更加的簡單,而不需要顯式的 lock。所謂阻塞,也就是如果讀取(value := <-ch)它將會被阻塞,直到有數據接收。其次,任何發送(ch<-5)將會被阻塞,直到數據被讀出。無緩衝 channel 是在多個 goroutine 之間同步很棒的工具。
Go 也允許指定 channel 的緩衝大小,很簡單,就是 channel 可以存儲多少元素。ch:= make (chan bool, 4),創建了可以存儲 4 個元素的 bool 型 channel。在這個 channel 中,前 4 個元素可以無阻塞的寫入。當寫入第 5 個元素時,代碼將會阻塞,直到其他 goroutine 從 channel 中讀取一些元素,騰出空間。
select
select可以監聽channel上的數據流動。select默認是阻塞的,只有當監聽的 channel 中有發送或接收可以進行時纔會運行,當多個 channel 都準備好的時候,select 是隨機的選擇一個執行的。
select {
case i := <-c:
// use i
default:
// 當 c 阻塞的時候執行這裏
}
select其實就是類似 switch 的功能,default 就是當監聽的 channel 都沒有準備好的時候,默認執行的(select 不再阻塞等待 channel)。
我們可以使用select來設置超時以避免goroutine阻塞導致的整個程序阻塞。
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v := <- c:
println(v)
case <- time.After(5 * time.Second):
println("timeout")
o <- true
break
}
}
}()
<- o
}