進程和線程react
併發和並行git
協程和線程github
能夠說,協程與線程主要區別是它將再也不被內核調度,而是交給了程序本身而線程是將本身交給內核調度,因此也不難理解golang中調度器的存在。golang
示例:多線程
package main import "fmt" import "time" func test() { var i int for { fmt.Println(i) time.Sleep(time.Second) i++ } } func main() { go test() for { fmt.Println("i' running in main") time.Sleep(time.Second) } }
goroutine調度模型併發
golang的goroutine是如何實現的? 知乎上一篇介紹文章。ui
圖中看到,當一個OS線程M0陷入阻塞時,P轉而在OS線程M1上運行。調度器保證有足夠的線程來運行因此的context P。spa
三者關係的宏觀的圖爲:操作系統
如何設置golang運行的cpu核數線程
package main import ( "fmt" "runtime" ) func main() { num := runtime.NumCPU() runtime.GOMAXPROCS(num) fmt.Println(num) }
備註:go1.8版本以後能夠不用設置,默認使用全部CPU核數。
鎖示例:
package main import ( "fmt" "sync" "time" ) var ( m = make(map[int]uint64) lock sync.Mutex ) type task struct { n int } func calc(t *task) { var sum uint64 sum = 1 for i := 1; i < t.n; i++ { sum *= uint64(i) } fmt.Println(t.n, sum) lock.Lock() m[t.n] = sum lock.Unlock() } func main() { for i := 0; i < 16; i++ { t := &task{n: i} go calc(t) } time.Sleep(10 * time.Second) lock.Lock() for k, v := range m { fmt.Printf("%d! = %v\n", k, v) } lock.Unlock() }
goroutine中使用recover
應用場景,若是某個goroutine panic了,並且這個goroutine裏面沒有 捕獲(recover),那麼整個進程就會掛掉。因此,好的習慣是每當go產 生一個goroutine,就須要寫下recover。
package main import ( "fmt" "runtime" "time" ) func test() { defer func() { if err := recover(); err != nil { fmt.Println("panic:", err) } }() var m map[string]int m["stu"] = 100 } func calc() { for { fmt.Println("i'm calc") time.Sleep(time.Second) } } func main() { num := runtime.NumCPU() runtime.GOMAXPROCS(num - 1) go test() for i := 0; i < 2; i++ { go calc() } time.Sleep(time.Second * 10000) }
Go併發原理 https://i6448038.github.io/2017/12/04/golang-concurrency-principle/
Golang非CSP併發模型外的其餘並行方法總結 https://i6448038.github.io/2018/12/18/Golang-no-csp/