這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
go語言開發入門
每個Go程式包含一個名為main的包以及其main函數,在初始化後,程式從main開始執行,避免引入不使用的包(編譯不通過)
基礎文法
基礎資料型別 (Elementary Data Type)
bool, byteint,int8,int16,int32,int64uint8,uint16,uint32,uint64 float32,float64(沒有float類型)string
變數賦值
var aa = 12a := 12a,b :=12,23
常量賦值
const( a = iota b)
迴圈只有 for
for i:=0;i<10;i++{ do Something...}for{ //相當於while(true){}}for _,v:= range list{ //range用法返回list數組或者列表的k,v兩個值(k數組索引從0開始,v值) fmt.Print("%s", v)}
選擇 if 和 switch
a := 1b := 1if a+b > 2 { // 註:if(條件不帶括弧,且左大括弧與if保持同行)}
完整demo 大白加大白等於白胖胖.求大,白,胖各是數字多少?
package mainimport ( "fmt" "strconv")/* 大白加大白等於白胖胖.求大,白,胖各是數字多少? */func main() { for d := 1; d < 10; d++ { for b := 1; b < 10; b++ { for p := 1; p < 10; p++ { daBai, _ := strconv.Atoi(strconv.Itoa(d) + strconv.Itoa(b)) // fmt.Println("大白", daBai) baiPangPang, _ := strconv.Atoi(strconv.Itoa(b) + strconv.Itoa(p) + strconv.Itoa(p)) // fmt.Println("白胖胖", baiPangPang) if daBai+daBai == baiPangPang { fmt.Println("-------------------大 白 胖--------------------") fmt.Printf("大 = %d, 白 = %d, 胖 = %d\n", d, b, p) fmt.Println("白胖胖: ", baiPangPang) } } } }}
資料(array)與切片(slice)
數組聲明:ArrayType = "[" ArrayLength "]" ElementType .
var a [32] intvar b [3][5] int
(1)數組是實值型別。將一個數組賦值給另一個,會拷貝所有的元素.
(2) 如果你給函數傳遞一個數組,其將收到一個數組的拷貝,而不是它的指標.
(3)數組的大小是其類型的一部分,類型[10]int和[20]int是不同的。數組長度在聲明後,就不可更改.
切片聲明:SliceType = "[" "]" ElementType .
var a []int
(1)沒有初始化的slice為nil.
(2)切片(slice)對數組進行封裝,實際上,切片可以看成大小可以動態變化的數組.
(3)Go中大多數的數組編程都是通過切片完成,而不是簡單數組.
一般來說,有兩種方式來初始化切片:
//通過數組方式var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}var mySlice []int = myArray[:5]//make方式make([]T, length, capacity)
Channel
Channel聲明:
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
var ch chan int
var ch1 chan<- int //ch1隻能寫
var ch2 <-chan int //ch2隻能讀
//make初始化
putnamechan := make(chan string, 100) //帶緩衝
Goroutine
關鍵字go,啟用Goroutine的唯一途徑
//同時向五個人打招呼func main(){ names := []string{"Eric","Tom","Jack","Jim"} for _,name:= range names{ go func(){ fmt.Printf("Hello,%s.\n",name) }() } runtime.Gosched()}
調度模型
圖中看,有2個物理線程M,每一個M都擁有一個context(P),每一個也都有一個正在啟動並執行goroutine。
P的數量可以通過GOMAXPROCS()來設定,它其實也就代表了真正的並發度,即有多少個goroutine可以同時運行。
圖中灰色的那些goroutine並沒有運行,而是出於ready的就緒態,正在等待被調度。P維護著這個隊列(稱之為runqueue),
Go語言裡,啟動一個goroutine很容易:go function 就行,所以每有一個go語句被執行,runqueue隊列就在其末尾加入一個
goroutine,在下一個調度點,就從runqueue中取出一個goroutine執行。
其他
- defer語句:調用一個被 defer 的函數時在函數剛要返回之前順延強制
- panic/recover 異常處理語句
缺陷
- 編譯產生的可執行檔尺寸非常大
- 代碼可讀性差(x,y,z int, a []*struct)
- go的濫用導致不可預見bug(注goroutine的使用應該是保守型的)
有兩種思維邏輯會想到使用goroutine
比如一個伺服器,接收請求,阻塞式的方法是一個請求處理完成後,才開始第二個請求的處理。其實在設計的時候我們一定不會這麼做,我們會在一開始就已經想到使用並發來處理這個情境,每個請求啟動一個goroutine為它服務,這樣就達到了並行的效果。這種goroutine直接按照思維的邏輯來使用goroutine
一個情境是這樣:需要給一批使用者發送訊息,正常邏輯會使用
for _, user := range users { sendMessage(user)}//但是在考慮到效能問題的時候,我們就不會這樣做,如果users的個數很大,比如有1000萬個使用者?我們就沒必要將1000萬個使用者放在一個routine中運行處理,考慮將1000萬使用者分成1000份, //每份開一個goroutine,一個goroutine分發1萬個使用者,這樣在效率上會提升很多。這種是效能最佳化上對goroutine的需求
執行個體httpmq
http協議實現的訊息佇列,資料持久化levelDb
//httpmq mux := http.NewServeMux()mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {} //net/http包起路由做攔截處理(put,get,status,view)//接收name\data參數//起四個channelputnamechan := make(chan string, 100) //入隊name管道putposchan := make(chan string, 100) //入隊資料索引管道getnamechan := make(chan string, 100) //出隊name管道getposchan := make(chan string, 100) //出隊資料索引管道//兩個Goroutine做訊息管道流入隊出隊操作go func(chan string, chan string) { for { name := <-putnamechan // putpos := httpmq_now_putpos(name) putposchan <- putpos }}(putnamechan, putposchan)go func(chan string, chan string) { for { name := <-getnamechan getpos := httpmq_now_getpos(name) getposchan <- getpos }}(getnamechan, getposchan)//put入隊操作putnamechan <- nameputpos := <-putposchanqueue_name := name + putposif data != "" { db.Put([]byte(queue_name), []byte(data), nil) } else if len(buf) > 0 { db.Put([]byte(queue_name), buf, nil)}//get出隊操作getnamechan <- namegetpos := <-getposchanif getpos == "0" { w.Write([]byte("HTTPMQ_GET_END"))} else { queue_name := name + getpos v, err := db.Get([]byte(queue_name), nil) if err == nil { w.Header().Set("Pos", getpos) w.Write(v) } else { w.Write([]byte("HTTPMQ_GET_ERROR")) }}
put操作
name --> putnameChannel --> 產生putPos --> putposChannel --> name+pos產生key持久化data
get操作
name --> getnameChannel --> 產生getPos --> getposChannel --> name+pos產生key getData