并发之goroutine
并发和并行的区别
goroutine的启动
将要并发执行的任务包装成一个函数,调用函数的时候,前面加上go关键字,就能够开启一个goroutine去执行该函数的任务。
goroutine对应的函数执行完,改goroutine就结束了。
程序启动的时候会自动创建一个goroutine去执行main函数
main函数结束了,那么程序就结束了,由该程序启动的所有其他goroutine也就都结束了。
goroutine的本质
goroutine的调度模型:GMP
M:N 把m个goroutine分配给n个操作系统线程
goroutine与操作系统线程(os线程)的区别
goroutine是用户态的线程,比内核态的线程更轻量级一点,初始时只占用2KB的栈空间。可以轻松开启数十万的goroutine也不会崩内存
runtime.GOMAXPROCS
Go1.5之后默认就是操作系统的逻辑核心数,默认跑满CPU
runtime.GOMAXPROCS(1) 只占用1核
work pool 模式
开始一定数量的goroutine去干活
for w := 0; w < 3; i++ {
go work(w, jobs, results)
}
sync.WaitGroup
var wg sync.WaitGroup
wg.Add(1) // 计数器+1
wg.Done() // 计数器-1
wg.Wait() // 等待结束
channel
为什么需要channel?
通过channel实现多个goroutine通信。
CSP:通过通信来共享内存
channel是一种类型,一种引用类型,make函数初始化后才可以使用。(slice, map, channel)
channel声明:
var ch chan 元素类型
channel的初始化:
ch = make(chan 元素类型, [缓冲区大小])
channel操作:
发送: ch <- 100
接收: x := <- ch
关闭: close(ch)
带缓冲区的通道和不带缓冲区的通道:
从通道取值
关闭已经关闭的channel,也会引发panic
单向通道:
通常是用做函数的参数
只读通道: <- chan int
只写通道: chan int <-
select(多路复用)
同一时刻有多个通道要操作的场景下,使用select