golang|GO语言goroutine

前言 go语言对于并发编程有原生的支持。
如下代码:创建10个协程并发执行。

package mainimport ( "fmt" "time" )func main() { for i := 0; i < 10; i++ { go func(i int) { fmt.Printf("hello from goroutine %d\n", i) }(i) } //main函数执行完,协程也会被终止,等待1秒,协程执行完 time.Sleep(time.Second) }

Courotine 协程: 1. go语言中开辟的实际是协程,协程是轻量级线程。作用和线程差不多,并发执行任务。
在go语言中可以开辟很多协程,比如:1000个协程,但是开辟1000个线程消耗的资源就比较多了。
2. 协程为什么是轻量级线程。
go语言中的协程是非抢占式多任务处理,由协程主动交出控制权。
抢占式式多任务处理:操作系统由更高优先级的任务,可以停止当前执行的线程,去执行更高优先级线程。
非抢占式多任务处理:操作系统不能主动去停止。而是由任务主动交出控制权。
线程是抢占式多任务处理,所以等任务切换时,需要内存来保存程序执行的上下文。
协程不需要那麽多内存来保存上下文,只需要保存切换点。
协程是编译器/解释器/虚拟机层面的多任务,不是操作系统层面的多任务。操作系统层面没有协程,
所以协程是轻量级线程。
【golang|GO语言goroutine】3. 多个协程可能在一个或者多个线程上执行。
我们知道操作系统有调度器,而go语言也有自己的调度器,来调度协程的运行。所以可能协程在不同的一个或者一个线程上运行。

参数传递给协程的注意点:
代码:
每次开辟的协程参数i需要被传入,如果不传入,会出现越界错误。
因为使用的i在全局只有一份,当i被加到10时,有的协程还没有运行完或者正在运行,此时i时同一个,再执行a[i]时,就越界了,作为参数传入,i会被拷贝一份,每个协程私有。
package mainimport ( "fmt" "time" )func main() { var a [10]int for i := 0; i < 10; i++ { go func(i int) { for { a[i]++ } }(i) // 会吹按越界错误 // go func() { //for { //a[i]++ //} // }() } //main函数执行完,协程也会被终止,等待1秒,协程执行完 time.Sleep(time.Second) fmt.Println(a) }

可以在命令行中可以输入go run -race xxx, 查看访问冲突。
golang|GO语言goroutine
文章图片


go语言的调度器 子程序(普通函数)是协程的特例。
golang|GO语言goroutine
文章图片

  1. 任何函数前加上go关键字,就能送给调度器调度,
  2. 虽然协程是非抢占式的,但是为了提高效率,调度器会在合适的点进行切换,不需要显示去切换。
一般的切换点:并不保存会切换,也不能保证在别的地方不切换。
  • I/O操作,Select
  • channel
  • 等待锁
  • 函数调用(有时)
  • runtime.Gosched(),显示切换
go语言的协程可能运行在一个线程里,也可能多个协程运行在一个线程里,也有可能多个协程在多个线程里,这由go语言调度器来决定。
golang|GO语言goroutine
文章图片



    推荐阅读