go 协程
并发与并行
并发:并发是指立即处理多个任务的能力,在一个时间段内,可以进行多个任务(干多个活),单核cpu,多个任务执行,只能每个任务执行一会,这就是并发
并行:并行指的是同时处理多个任务,在同一时刻,可以进行多个任务,必须有多个cpu的支持
针对python语言,cpython解释器中的GIL锁,会导致同一时刻,只能有一个线程执行。这一点从python语言诞生之初就携带而来,也是如今经常被诟病的一种设计,导致python的效率不高,并发能力不行,无法利用多核优势。虽然python中也有各种扩展包来实现多进程(multiprocess.process),多线程(threading.Thread),协程(gevent)–>单线程下的并发,但是GIL锁+边编译边执行的方式,天然让效率大打折扣,而且还不好用。在python中对于并发有很有深意的两句话:
- IO密集型,开多线程
- 计算密集型, 开多进程
go协程的实现
但是到了go语言中,不存在这个问题,go中没有进程,线程的概念,通通称为协程。go中开多个协程,既能实现单线程下的并发,又能够很好的利用多核优势。
go 协程是与其他函数或方法一起并发运行的函数或方法。go 协程可以看作是轻量级线程。与线程相比,创建一个 go 协程的成本很小。因此在 go 应用中,常常会看到有数以千计的 go 协程并发地运行。
go协程的本质就是:线程池+协程
go 开启并发关键字: go + 函数()
示例如下:
func main() {
Myprint()
go Myprint() // 开启一个 goroutine
}
func Myprint() {
fmt.Println("1")
}
只会打印出一个值,因为另外一个 goroutine
还没来得及打印,主goroutine
就结束了。主goroutine
等待一秒,另一个goroutine
的打印就能输出到控制台来了。
func main() {
Myprint()
go Myprint()
time.Sleep(1* time.Second) // 等待1s
}
func Myprint() {
fmt.Println("1")
}
可以同时开启多个goroutine,尽管用,开上万个都没问题,具体原因会在 GMP 模型一文中简述
func main() {
Myprint()
go Myprint()
go Myprint()
go Myprint()
go Myprint()
go Myprint()
time.Sleep(1* time.Second) // 等待1s
}
func Myprint() {
fmt.Println("1")
}
验证goroutine能够利用多核优势
以python语言做对比,因为GIL锁,python默认是单线程执行的,执行下方代码,你会发现你的CPU使用涨幅不会很大, 如果是6核cpu,python无论开多少线程都只会占 1/6
from threading import Thread
i = 0
while True:
t = Thread(target=print,args=(i, ))
i += 1
但是换成下方go代码,cpu利用率很快就会接近100%
for {
go fmt.Println(1)
}