----------------------------------------- go 並發
// 註解:go 語言天生為程式並發所設計,可以說go的強項就是在cpu並發上的處理。// go 語言層面就支援了並發。(不是一般進階語言的多線程並發,是系統級真實並發)// go 語言通過安全的通道發送和接受資料以實現同步// 一般情況下,一個普通的案頭電腦跑十幾二十幾個線程就有點負載過大了,但是同樣的硬體裝置go可以輕鬆上K。
----------------------------------------- goroutine
// 註解:go 並發設計的核心,goroutine在並發中起到的作用就是協程(CSP),但是它比線程更小。(協程=微線程)// go 不支援後台協程,意思就是主程式退出,協程跟著一起退出func newTask() { for { fmt.Println("new task ...") time.Sleep(time.Second) // 休眠1s }}func main() { go newTask() // 建立一個協程,建立一個任務 for { fmt.Println("main ...") time.Sleep(time.Second) // 休眠1s }}// result : // main... // new task ...// ....
----------------------------------------- gosched
// 註解:讓出CPU時間片,讓出當前 gorotine 的執行許可權,// 調度器安排其他等待任務運行,並在下次某個時候從該位置恢複執行。func main() { go func() { for i := 0; i < 5; i++ { fmt.Println("go") } }() for i := 0; i < 2; i++ { fmt.Println("hello") } // 這種情況,匿名函數未得到執行程式就結束了。 // 時間片案例示範代碼,修改如下: for i := 0 .... { runtime.Gosched() fmt.Println("hello") } // 執行結果: // go go ... hello ..}
----------------------------------------- goexit
import "runtime"// 註解:終止所在的協程 (所在的協程不是當前函數)func test() { defer fmt.Println("ccccc") runtime.Goexit() fmt.Println("dddd")}func main() { go func (){ fmt.Println("aaaa") test() fmt.Println("bbbb") } for { } // result: // aaaa cccc}
----------------------------------------- gomaxProcs
// 註解:設定可以並行計算的 CPU 核心數的最大值import "runtime"func main() { n := runtime.GOMAXProcs(1) //制定以1核運算 fmt.Println("n = ", n) for { go fmt.Print(1) fmt.Print(0) } // 列印結果:11111.. 一大片, 00000...一大片 // 如果設定 GOMAXProcs(4) 為 4 核交叉效果更好}
----------------------------------------- 資源爭奪問題 channel
// 註解:channel 也是一種資料類型,同步// 文法:channel <- value // 發送 value 資料到 channel// <- channel // 接收並丟棄// 案例:// 全域變數,建立一個 channelvar ch = make(chan int)// 定義一個印表機,參數為字串,按每個字元列印func Printer(str string) { for _, data := range str { fmt.Printf("%c", data) time.Sleep(time.Second) } fmt.Printf("\n")}func person1() { Printer("loong print") ch <- 666 // 給管道寫資料}func person2() { <- ch // 從管道取資料,如果管道沒有資料前他就會阻塞 Printer("make print")}func main() { // 建立 2 個協程,代表 2 個人,2 個人同時使用印表機 go person1() go person2()}// 註解: 【認真看】// 沒有 channel 的情況// 列印結果混亂,person1 列印一個h,person2 列印一個 w,交叉了。不符合我們的要求// 增加 channel ,則在 <-ch 的地方進行了阻塞,通過進、出的方式融合這種解決這種並發互搶資源的問題。
----------------------------------------- channel 實現同步和資料互動
fun main() { ch := make(chan string) defer fmt.Println("主協程也結束") go func() { defer fmt.Println("子協程調用完畢。") for i := 0; i < 2; i++ { fmt.Println("子協程 i=", i) time.Sleep(time.Second) } ch <- "我是子協程,工作完畢" } str := <-ch // 沒有資料前,阻塞 fmt.Println("str = ", str)}// 注意:程式需求:主程式結束之前,能夠完整執行匿名函數中的代碼// 使用 channel 配合完成
----------------------------------------- channel 無緩衝&有緩衝
c1 := make(chan int) 無緩衝c2 := make(chan int,1) 有緩衝c1 < -1 // 無緩衝:不僅僅是向 c1 通道放 1,// 而是一直要等有別的協程 <-c1 接手了這個參數,那麼c1<-1才會繼續下去,要不然就一直阻塞著。// 有緩衝: c2<-1 則不會阻塞,因為緩衝大小是1(其實是緩衝大小為0),// 只有當放第二個值的時候,第一個還沒被人拿走,這時候才會阻塞。// 不需要再使用記得關閉channel close(c1)// 判斷管道是否關閉 if num, ok := <- c1; ok == true { // 關閉了 }----------------------------------------- channel 單方向var ch1 chan int // ch1 是一個正常的 channle,不是單向的var ch2 chan<- float64 // ch2 是單向 channel,只用於寫 float64 資料var ch3 <-chan int // ch3 是單向 channel,只用於讀取 int 資料// * 管道的操作,一定要避免死結的情況。
----------------------------------------- channel 應用
// 此案例可以應用很多情境,每寫一個,則可以消耗一個// 此通道只能寫,不能讀func producer(out chan<- int) { for i := 0; i < 10; i++ { out <- i * i } close(out)}// 此通道只能讀,不能寫func consumer(in <-chan int) { for num := range in { fmt.Println("num = ", num) }}func main() { // 建立一個雙向通道 ch := make(chan int) // 生產者,生產數字,寫入 channel // 開啟一個協程 go producer(ch) // 消費者,從channel讀取內容列印 consumer(ch)}
----------------------------------------- Timer
import ( "time" "fmt")func main() { // 建立一個定時器,設定時間為2s,2s後,往time通道寫內容 timer := time.NewTimer(2 * time.Second) fmt.Println("目前時間:", time.Now()) // 2s後,往timer.C寫資料,有資料後讀取 t := <-time.C // channel 沒有資料前後阻塞 fmt.Println("t = ", t)}
----------------------------------------- select
// 註解:go語言提供了一個關鍵字 select,通過 select 可以監聽 channel 上的資料流動// 文法:(類似 switch)select { case <-chan: // 如果channel 成功讀到資料,則進入 case 塊語句 case chan<- 1: // 如果channel 成功寫到資料,則進入 case 塊語句 default: // 如果上面都沒有成功,則進入default處理流程 // 注意:慎用,很消耗 cpu}