----------------------------------------- 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 核交叉效果更好 }
----------------------------------------- 資源爭奪問題 channelspa
// 註解:channel 也是一種數據類型,同步 // 語法:channel <- value // 發送 value 數據到 channel // <- channel // 接收並丟棄 // 案例: // 全局變量,建立一個 channel var 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 應用code
// 此案例能夠應用不少場景,每寫一個,則能夠消耗一個 // 此通道只能寫,不能讀 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 }