# 技術|Golang 筆記 ## (1) go routine - 輕量級的線程 ```go= package main import ( "fmt" "time" ) func printNumbers() { for i := 1; i <= 5; i++ { fmt.Println(i) time.Sleep(100 * time.Millisecond) } } func main() { go printNumbers() // 開啟新的 Goroutine 執行 printNumbers 函數 fmt.Println("Goroutine started") time.Sleep(1 * time.Second) // 等待 Goroutine 完成 } ``` ## (2) channel - `channel` 在 goroutine 中實現同步和數據的傳遞 ```go= package main import "fmt" func sum(a []int, c chan int) { total := 0 for _, v := range a { total += v } c <- total // 將結果發送到 channel } func main() { a := []int{1, 2, 3, 4, 5} c := make(chan int) go sum(a, c) result := <-c // 從 channel 接收結果 fmt.Println("Sum:", result) } ``` ## (3) context - `context` 用來在 Goroutine 之間傳遞數據、信號和取消指令 ```go= package main import ( "context" "fmt" "time" ) func task(ctx context.Context) { select { case <-time.After(2 * time.Second): fmt.Println("Task completed") case <-ctx.Done(): // 任務取消或超時 fmt.Println("Task cancelled:", ctx.Err()) } } func main() { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() // 確保取消函數在 main 結束時被調用 go task(ctx) time.Sleep(3 * time.Second) } ``` ## (4) defer - `defer` 用於確保資源釋放,延遲執行某個語句,按 LIFO (Last In First Out) 順序執行 ```go= package main import ( "fmt" "os" ) func main() { file, err := os.Open("test.txt") if err != nil { fmt.Println(err) return } defer file.Close() // 確保在函數結束時關閉文件 fmt.Println("File opened successfully") } ``` ## (5) switch - 接口 interface{} 型別判斷 variable.(type),重點是會`依照 case 順序依序執行` ```go= func convert(i interface{}) { switch t := i.(type) { case int: println("i is interger", t) case string: println("i is string", t) case float64: println("i is float64", t) default: println("type not found") } } ``` ## (6) select - 只能接 channel 否則會出錯,同一個 channel 在 select 會隨機選取,default 會直接執行 ```go= func main() { ch := make(chan int, 1) ch <- 1 select { case <-ch: fmt.Println("random 01") case <-ch: fmt.Println("random 02") default: fmt.Println("exit") } } ``` ## Golang 概念 - 函數外的每個語句都必須以關鍵字開始 - make 和 new 差異 [Ref](https://stackoverflow.com/questions/25358130/what-is-the-difference-between-new-and-make) - make 用在 slices, maps, 和 channels,回傳物件本身 - new 對任何 type 都適用,回傳指標 - [並發](https://peterhpchen.github.io/2020/03/08/goroutine-and-channel.html)同步機制: (1) Waitgroup (2) channel (3) mutex ## Methods 方法/ Interface 接口 - (1) 為某資料型態 (預設的或是自訂義struct) 建立 Method - receiver 可設定成值或是指標,要用正常的 pointer/reference 去操作 ```go= type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := Vertex{3, 4} p := &Vertex{4, 3} (&v).Scale(2) p.Scale(2) fmt.Println(v, p) // {6 8} &{8 6} fmt.Println(v.Abs(), (*p).Abs()) // 10 10 } ``` - `以下是 GO 當中的例外,個人認為這個特性很糟` ```go= func main() { v := Vertex{3, 4} p := &Vertex{4, 3} v.Scale(2) // 會被自動解讀為 (&v).Scale(2) fmt.Println(v) fmt.Println(p.Abs()) // 會被自動解讀為 (*p).Abs() } ``` - (2) Interface 是藉由定義一系列方法,可以變身成各種型態 ```go= type face interface { Scale(f float64) } type Vertex struct { X, Y float64 } // 標準寫法 (1) func (v *Vertex) Scale(f float64) var i face = &Vertex{3, 4} // 標準寫法 (2) func (v Vertex) Scale(f float64) var i face = Vertex{3, 4} // 底下寫法編譯會過,但看別人教學沒人這樣用 func (v Vertex) Scale(f float64) var j face = &Vertex{8, 6} // 不正確且編譯不過 func (v *Vertex) Scale(f float64) var i face = Vertex{3, 4} ``` ## 陣列/Slice - `陣列` ```go= arr := [4]int{1, 2, 3, 4} fmt.Println(arr) for _, v := range arr { fmt.Printf("%d ", v) } ``` - `Slice 切片` - 切片是一個有長度和容量,類似陣列的結構 - 動態宣告、初始化 ```go= a := make([]int, 5) a := []int{1, 2, 3, 4, 5} func printSlice(s []int) { fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s) } ``` - (1) 使用 `[a:b]` 只是一個參照 ```go= a := []int{1, 2, 3, 4, 5} b := a[2:4] a[2] = 100 b[1] = 99 printSlice(a) printSlice(b) //---------------------------- len=9 cap=9 [1 2 100 99 5 6 7 8 9] len=2 cap=7 [100 99] ``` - (2) 使用 append 要注意參照的問題 ```go= arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 8, 7} arr2 := arr[2:4] printSlice(arr) printSlice(arr2) arr2 = append(arr2, -1) arr = append(arr, 6) printSlice(arr) printSlice(arr2) //----------------------------- len=10 cap=10 [1 2 3 4 5 6 7 8 8 7] len= 2 cap=8 [3 4] len=11 cap=20 [1 2 3 4 -1 6 7 8 8 7 6] len= 3 cap=8 [3 4 -1] ``` - (3) 使用 copy,只會改到 ```go= a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} b := a[2:4] c := []int{0, 0, 0, 0, 0, 0} printSlice(a) printSlice(b) copy(b, c) printSlice(a) printSlice(b) //---------------------------- len=9 cap=9 [1 2 3 4 5 6 7 8 9] len=2 cap=7 [3 4] len=9 cap=9 [1 2 0 0 5 6 7 8 9] len=2 cap=7 [0 0] ```