刚开始学习Go,看到gorountine和channel的时候试了试下面这个例子:
package main
import (
"fmt"
)
func display(msg string, c chan bool) {
fmt.Println("display first message:", msg)
c <- true
}
func main() {
c := make(chan bool)
go display("hello", c)
go display("world", c)
<-c
}
这样的话会输出两行结果:
display first message: hello
display first message: world
但是我总觉得应该只输出一行结果呢?main当中我定义了一个channel,之后分支出去两个gorountine去执行display函数,最后一行<-c
阻塞等待数据。那么其中一个gorountine在打印完结果并向c发送数据后,main就接受到了数据,就应该继续执行然后结束了呀。怎么会打出两行结果呢?是不是我哪里理解的有问题?求指导,谢谢!
From goroutine scheduler
From understanding goroutines
也就是说如果没有发生IO等需要挂起和谦让执行机会的时候,goroutine是不会终止的,除非执行完毕。
以我的理解来说明下这个问题。 主要问题是,这里虽然看似是开启了并发的模式,但实际上是单线程的,因为你没有设置GOMAXPROCS(默认是1,如果环境变量GOMAXPROCS也没有设置的话,该数值包含main thread),而且这些简单函数都不会导致阻塞操作,于是这样这些函数就是顺序执行的,没有并发效果。我们把你的例子修改一下改成下面这个
上面这个运行的结果还是会产生两个语句,但是你会发现即使把display2的chan操作注释掉这里依然会输出两次,和chan的是否阻塞是没有关系的,而且没有发生需要挂起的操作。 我们修改成第二个示例
这个时候的输出就只有display1了,这是因为我们的goroutine发现display2可以挂起,于是交出CPU(此时两个goroutine是执行在同一个底层thread里面的),这时候程序继续执行顺利结束。 那如果我们的display2不愿意交出CPU呢?比如下面,我们在display2上绑定了一个CPU密集型的操作,这个时候我们的程序就会一直等,等到display2执行完才会结束,因为它无法交出CPU。
那我们不禁要问了那goroutine的优势在什么地方呢,如果它不能解决CPU的正常让出的话。首先要明确一个问题就是GO中影响groutine的重要参数GOMAXPROCS,系统默认是1,它决定的是底层可以用来运行goroutine的最大thread数目,这就是这里无法让出的原因,so,如果我们改成GOMAXPROCS大于1,上面的问题就解决了,因为我们的goroutine现在运行在两个不同的thread里面,如下,我们的程序不会等待display2就可以顺利结束。