Go goroutine
并发性Concurrency
并发概念
Go是并发语言,而非并行语言。并发性是同时处理许多事情的能力。并行性parallelism指可以同时做很多事情。
Go中使用Goroutines来实现并发concurently。Goroutines是与其他函数或方法同时运行的函数或方法。
goroutines
Goroutines被认为是轻量级的线程,与线程相比,创建Goroutines成本很小,故Go应用程序可以并发执行数千个Goroutines。
在线程上优势
- 与线程相比,Goroutines非常便宜,只是堆栈大小的几个kb,堆栈可根据应用程序的需要增长和收缩。而线程情况下,堆栈大小必须指定并且是固定的。
- Goroutines被多路复用到较少的OS线程,一个程序中可能只有1个线程与数千个Goroutines,若线程中任何Goroutine都表示等待用户输入,则会创建另一个OS线程,剩下的Goroutines被转移到新的OS线程。所有这些都由运行时处理,程序员从这些复杂细节中抽象出来,得到一个与并发工作相关的干净的API。
- 使用Goroutines访问共享内存时,通过设计的通道可以防止竞态条件发生。通道可以被认为时Goroutines通信的管道。
Go语言协程
定义
不需要在定义时区分是否是异步函数(这点是针对Python3.5引入的async def定义时说明自己是一个协程)
调度器在合适的点进行切换(非抢占式的,但切换点也不是任意的点,传统是需要显示写出来,goroutine不需要)
可能切换的点:I/O,select ; channel ;等待锁;函数调用(有时);runtime.Gosched()
注意:以上只是参考,不能保证切换,不能保证在其他地方不切换
使用-race来检测数据访问冲突
使用
任何函数或方法调用前面加上go就能送给调度器运行 ,运行一个新的Goroutine。
例子:
例子1:
goroutine.go
输出是:Hello from goroutine 随机打印的值
查看系统究竟起了多少个线程?
top命令查看:CPU 占用率 #TH线程(根据系统核来)
Goroutine协程
子程序是协程的一个特例
协程特点:
轻量级”线程”
非抢占式多任务处理,由协程主动交出控制权
编译器/解释器/虚拟机层面的多任务
多个协程可在一个或多个线程在工作
启动多个Goroutine
例子1
func numbers() { for i := 1; i <= 5; i++ { time.Sleep(250 * time.Millisecond) fmt.Printf("%d ", i) } } func alphabets() { for i := 'a'; i <= 'e'; i++ { time.Sleep(400 * time.Millisecond) fmt.Printf("%c ", i) } } func main() { go numbers() go alphabets() time.Sleep(3000 * time.Millisecond) fmt.Println("main terminated") } 输出:1 a 2 3 b 4 c 5 d e main terminated
例子2
普通函数与协程对比:doWork做完了才把控制权交给main。
Goroutine规则:
- 当新Goroutine开始时,Goroutine调用立即返回,且不等待Goroutine执行结束。
- 当Goroutine调用,并且其任何返回值被忽略之后,go立即执行到下一行代码
- main的Goroutine应该为其他的Goroutines执行,若main的Goroutine终止了,程序将被终止,其他Goroutine将不会运行。
例子:
func hello() { fmt.Println("Hello world goroutine") } func main() { go hello() //由于main进行Sleep,故go hello()有足够时间在main Goroutine终止之前执行 time.Sleep(1 * time.Second) fmt.Println("main function") } //输出是:Hello world goroutine main function
其他语言对协程支持
Go语言原生支持
C++:通过Boost.Coroutine库支持
Java:不支持 (并不是完全不支持,有第三方的)
Python中:Python3.5以前使用yield关键字实现协程,Python3.5加入了async def 对协程原生支持