文章目錄
- 用 channel 作為并發小容器
- channel 的遍歷
- channel 導致的死鎖問題
- 用 channel 傳遞信號
- 用 channel 并行處理文件
- 用channel 限制接口的并發請求量
- 用 channel 限制協程的總數量
用 channel 作為并發小容器
- 注意這里的 ok 如果為 false,表示此時不僅channel為空,而且channel已經被關閉了
channel 的遍歷
- 注意,遍歷會使頭指針往后移,相當于取走元素
- 如果 close channel 注釋掉,也不會報錯,但是會阻塞,導致輸出bye bye這一句代碼得不到執行
- 遍歷的另外一種寫法
channel 導致的死鎖問題
- 上面的main方法是等3秒鐘結束main協程,更好的方法是使用waitGroup
- 如果 close channel 注釋掉,程序執行的時候會報錯
fatal error: all goroutines are asleep - deadlock!
- travese和main都阻塞了
用 channel 傳遞信號
- 可以用向channel中傳遞信號,代替waitGroup,優雅地等子協程結束
struct{}
空結構體類型,空結構體實例struct{}{}
- 空結構體在go語言里是一種特殊的結構體,go語言通過一個統一的引用變量來表示所有的空結構體,而且不占用任何的內存空間
- 使用空結構體,語義會更加明確且不占內存
- 在 Go 語言(Golang)中,reflect 是一個非常強大的包,提供了 運行時反射機制,可以在運行時檢查變量的類型、獲取或設置變量的值。
// 獲取類型和值var x int = 42
t := reflect.TypeOf(x) // reflect.Type
v := reflect.ValueOf(x) // reflect.Valuefmt.Println("類型:", t) // int
fmt.Println("值:", v.Int()) // 42
// 修改變量的值(需要傳指針)var x int = 10
v := reflect.ValueOf(&x) // 注意要傳指針
v.Elem().SetInt(100) // 修改值
fmt.Println("x的新值:", x) // 100
// 檢查變量類型func checkType(i interface{}) {t := reflect.TypeOf(i)switch t.Kind() {case reflect.Int:fmt.Println("是整數")case reflect.String:fmt.Println("是字符串")default:fmt.Println("其他類型")}
}
// 結構體字段操作type Person struct {Name stringAge int
}p := Person{"Tom", 30}
v := reflect.ValueOf(p)
t := reflect.TypeOf(p)for i := 0; i < t.NumField(); i++ {field := t.Field(i)value := v.Field(i)fmt.Printf("%s: %v\n", field.Name, value)
}
用 channel 并行處理文件
- channel 不僅可以當作數據容器使用,也可以當作信號容器來使用
- 下面這個例子是要把多個txt文件合并為一個txt文件,一個常規的思路就是我們順序讀取文件,每讀取一行就把這一行對應地寫入到新的文件里面去,但是io操作很消耗時間,且三個文件毫不相關,怎么加速?
- 考慮三個goroutine并行讀,且往一個buffer channel寫數據,由一個goroutinue往buffer channel里讀數據寫入文件,這樣可以協調讀者和寫者的速度不匹配問題
- 初始化pc_sync里面有3個元素,協程完成后會從pc_sync取走一個元素,當所有協程結束后,pc_sync為空
用channel 限制接口的并發請求量
- 有時候我們需要限制微服務接口的并發請求度,因為有些接口會涉及到大量的cpu計算或者是內存開銷,如果瞬間并發度太大的話,服務器負載會很高
- 但是如果限制了的話必然會犧牲用戶的體驗
- 用channel的阻塞機制實現
用 channel 限制協程的總數量
- 需要封裝,對于所有的協程創建都去走一個統一的入口
- ticker 實際上就是每隔一段時間會給ticker.C中放入一個元素