在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
进程和线程
并发和并行
协程和线程
可以说,协程与线程主要区别是它将不再被内核调度,而是交给了程序自己而线程是将自己交给内核调度,所以也不难理解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是如何实现的? 知乎上一篇介绍文章。
图中看,有2个物理线程M,每一个M都拥有一个context(P),每一个也都有一个正在运行的goroutine。
P的数量可以通过GOMAXPROCS()来设置,它其实也就代表了真正的并发度,即有多少个goroutine可以同时运行。 图中灰色的那些goroutine并没有运行,而是出于ready的就绪态,正在等待被调度。P维护着这个队列(称之为runqueue)。 图中看到,当一个OS线程M0陷入阻塞时,P转而在OS线程M1上运行。调度器保证有足够的线程来运行所以的context P。 三者关系的宏观的图为:
如何设置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/
|
请发表评论