package main
import (
"fmt"
)
func main() {
for i := 0; i < 10; i++ {
go func() {
fmt.Println(i)
}()
}
}
不会打印任何内容:
当程序执行到一条 go 语句时,Go 语言运行时系统会试图从某个存放空闲的 goroutine 的队列中获取一个 goroutine,找不到时才会创建一个 goroutine。
然后 Go 语言运行时系统用该 goroutine 包装当前的 go 函数,再将该 goroutine 追加到存放某个可运行的 goroutine 队列中。
因此,go 函数的执行时间总是会滞后于所属的 go 语句的执行时间。
而 for 语句会以很快的速度执行完毕,但那 10 个包装了 go 函数的 gorouting 往往还没获得运行的机会。
一旦主 goroutine 中的代码执行完毕,当前的 Go 程序就会结束运行。
让主 goroutine 等待其它 goroutine
package main
import (
"fmt"
)
func main() {
num := 10
// struct{} 代表既不不含任何字段也不拥有任何方法的空结构体类型
// 仅仅把通道当作传递某种简单信号的介质时,struct{} 作为元素类型是最好的
sign := make(chan struct{}, num) // 通道长度与 goroutine 数量一致
for i := 0; i < num; i++ {
go func() {
fmt.Println(i)
// 发送表达式应该在 go 函数体的最后面
// struct{} 的方法只有一个,即 struct{}{},占用的内存空间是 0 字节
sign <- struct{}{}
}()
}
// 从通道接收元素值,接收的次数应当与 goroutine 数量一致
for j := 0; j< num; j++ {
<- sign
}
}
让多个 goroutine 按既定的顺序运行
如何在使用 goroutine 的情况下,让上述打印的结果为 0 到 9?
package main
import (
"fmt"
"sync/atomic"
"time"
)
func main() {
var count uint32 // count 成为一个信号,总是下一个可以调用打印函数的 go 函数的序号
trigger := func(i uint32, fn func()) {
for {
// 不断获取一个名叫 count 变量的值
// 如果该值与参数 i 相等,则调用 fn 函数
if n := atomic.LoadUint32(&count); n == i {
fn()
// 将 count 变量的值加 1
// 因为 trigger 函数会被多个 goroutine 并发调用
// count 并非本地变量,所以对它的操作会产生竞争条件
atomic.AddUint32(&count, 1)
// 显式退出当前循环
break
}
// 如果该值与参数 i 不等
// 让当前 goroutine 睡眠一个纳秒再进入下一个迭代
time.Sleep(time.Nanosecond)
}
}
for i := uint32(0); i < 10; i++ {
// 接收一个参数
go func(i uint32) {
fn := func() {
fmt.Println(i)
}
trigger(i, fn)
}(i) // 调用时将变量 i 的值传递进去
}
// 让主 goroutine 最后一个运行完毕
trigger(10, func(){})
}