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.Millisecond)
}
goroutine其实是一种协程coroutine。
协程是轻量级的“线程”,线程是由OS进行调度的,因此在任何时候都可以被抢占的,而协程是非抢占式多任务处理,由协程主动交出控制权。编译器/解释器/虚拟机层面的多任务,不是操作系统层面的多任务,go语言有一个调度器来调度协程。多个协程可以在一个或者多个线程上运行。
func main() {
//main也是一个goroutine
var a [10]int
for i := 0; i < 10; i++ {
go func(i int) {
for {
a[i]++ //在这个goroutine里面没有机会交出控制权
}
}(i)
}
time.Sleep(time.Millisecond) //因为没人交出控制权,因次这句也执行不了
}
如何交出控制权呢?
I/O操作会交出控制权,还可以手动交出控制权runtime.Gosched()。
//此代码会出现 index out of range的错误 可以使用go run -race 源文件名 进行查错
//因为这里形成了闭包,匿名函数中的a[i]会引用自由变量i,当i=10,跳出循环的时候,调度到a[i]++的goroutine时就会出现index out of range,因此需要将变量作为参数传入。
func main() {
var a [10]int
for i := 0; i < 10; i++ {
go func() { //出现race condition条件竞争
for {
a[i]++
runtime.Gosched()
}
}()
}
time.Sleep(time.Millisecond)
fmt.Println(a)
}
//正确代码
func main() {
//有可能会出现,main在fmt.Println(a),有一个goroutine在a[i]++。出现race condition。需要使用channel来解决。
var a [10]int
for i := 0; i < 10; i++ {
go func(i int) {
for {
a[i]++
runtime.Gosched()
}
}(i)
}
time.Sleep(time.Millisecond)
fmt.Println(a)
}
子程序是协程的一个特例。
多个协程可能映射到同一个线程。
任何函数只需加上go就能送给调度器运行。
不需要在定义时区分是否是异步函数。
调度器在合适的点进行切换。
使用-race来检测数据访问的冲突。
以上只是参考,不能保证切换,不能保证在其他地方不切换。