协程Coroutine
- 轻量级“线程”
- 非抢占式多任务处理,由协程主动交出控制权
- 编译器/解释器/虚拟机层面的多任务
- 多个协程可以在一个或多个线程上运行
func main() {
var a [10]int
for i := 0; i < 10; i++ {
go func(i int) {
for {
a[i]++
}
}(i)
}
time.Sleep(time.Microsecond)
fmt.Println(a)
}
此代码会陷入到一个死循环中,因为goroutine没有主动交出控制权,此时使用查看任务管理器会发现
发现任务基本占满了CPU,这时候需要手动交出控制权
func main() {
var a [10]int
for i := 0; i < 10; i++ {
go func(i int) {
for {
a[i]++
// 交出控制权,不主动传出控制权会陷入死循环,这就是非抢占
runtime.Gosched()
}
}(i)
}
time.Sleep(time.Microsecond)
fmt.Println(a)
}
这时候程序不会再进入死循环而使得其他goroutine无法运行
还有一个问题,如果我们不定义i int的话
func main() {
var a [10]int
for i := 0; i < 10; i++ {
go func() {
for {
a[i]++
// 交出控制权,不主动传出控制权会陷入死循环,这就是非抢占
runtime.Gosched()
}
}()
}
time.Sleep(time.Microsecond)
fmt.Println(a)
}
这个时候会出现这样一个错误
panic: runtime error: index out of range
这时候可以使用go的一些命令行参数来进行查看问题
go run -race goroutine.go
会获取这样的输出
WARNING: DATA RACE
Read at 0x00c00000a0b0 by goroutine 6:
main.main.func1()
D:/1important/Go/GoPath/src/Godemo/W1/day3/goroutine/goroutine.go:14 +0x77
Previous write at 0x00c00000a0b0 by main goroutine:
main.main()
D:/1important/Go/GoPath/src/Godemo/W1/day3/goroutine/goroutine.go:11 +0x122
Goroutine 6 (running) created at:
main.main()
D:/1important/Go/GoPath/src/Godemo/W1/day3/goroutine/goroutine.go:12 +0xf8
意思是说,在goroutine 6中,这个参数被读取了,但是之前在main goroutine中这个参数被写了,这样造成的溢出错误。再看上边的代码的时候,会发现由于函数式编程的概念a[i]++
中的i和for循环中的i是同一个i,这样会造成i循环到10的时候虽然跳出了循环,但是在某个goroutine中i还是在被使用,这个时候的i是10,所以会产生数组溢出问题。
如果仔细观察会发现,在执行了go run -race goroutine.go
后还有一个数据问题
Read at 0x00c000018190 by main goroutine:
main.main()
D:/1important/Go/GoPath/src/Godemo/W1/day3/goroutine/goroutine.go:21 +0x102
Previous write at 0x00c000018190 by goroutine 6:
main.main.func1()
D:/1important/Go/GoPath/src/Godemo/W1/day3/goroutine/goroutine.go:14 +0x6f
Goroutine 6 (running) created at:
main.main()
D:/1important/Go/GoPath/src/Godemo/W1/day3/goroutine/goroutine.go:12 +0xca
这个因为在使用fmt.Println(a)
输出该数组的时候,还有某些goroutine在对数组a进行写入,这里需要使用channel来解决。
goroutine可能的切换点
- I/O,select
- channel
- 等待锁
- 函数调用(有时)
- runtime.Gosched()
- 只是参考,不能保证切换,不能保证其他地方不切换