这是我参与「第三届青训营 -后端场」笔记创作活动的第一篇笔记。
经过很多天的学习积淀后,我认为第一篇笔记应该总结一下goroutine和channel的用法。
第二讲中提到了并发编程中的四个要点
- 协程 Goroutine
- 通道 Channel
- 锁 Lock pkg.go.dev/sync
- 线程同步 WaitGroup pkg.go.dev/sync
它们在工程实践中不只是“经常会用到”那么简单,应该说是“天天都在用”,非常重要;而且非常容易出bug,属于是go语言编程的难点。
goroutine和channel也恰是go语言的精华所在——快捷高效实现并发编程。下面我们就来看看goroutine与channel的用法。
goroutine快速入门
func main() {
for i := 0; i < 1000; i++ {
go func(i int) {
for {
fmt.Printf("Hello from "+
"goroutine %d\n", i)
}
}(i)
}
time.Sleep(time.Minute)
}
此处开了1000个协程同时打印数字,此处让主程序睡眠1分钟,不然还没等协程打印主程序就退出了。这个方式显然不太优雅,而且协程调度大有问题,IO操作使得协程互相抢占,有一些数字顾及不到,根本打印不出来。
而且此处有一个问题,为什么非要让匿名函数接收这个i呢?如果不接收,程序就不安全。
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
var a [10]int
for i := 0; i < 10; i++ {
go func() {
for {
a[i]++
runtime.Gosched()
}
}()
}
time.Sleep(time.Millisecond)
fmt.Println(a)
}
看下面一个程序,运行会报Index out of range的错误。这是因为goroutine开的是协程,上面在跑,下面也在跑。当主程序跳出for循环后,i = 10,而在协程中a[10]是不存在的,所以会有这个错误。
goroutine本质
- goroutine是轻量级的线程
- goroutine能够做到非抢占式多任务处理,由协程主动交出控制权
- go语言是编译器/解释器/虚拟机层面的多线程
- 多个协程可能在一个或多个线程上运行
goroutine可能的切换点
- I/O, select(见第一个代码块)
- channel
- 函数调用(有时)
- runtime.Gosched(见第二个代码块)
- 等待锁
以上就是goroutine的注意事项,有什么疑问大家可以交流讨论