如果在不指定GOMAXPROCS环境变量的情况下运行Go程序,则会定期在单个OS线程中执行Go例程。但是,为了让程序看起来像是多线程的(这就是goroutines的用途,不是吗?),Go调度器有时必须切换执行上下文,因此每个goroutine都可以完成它的工作。
正如我所说的,当没有指定GOMAXPROCS变量时,Go运行时只允许使用一个线程,所以当goroutine执行一些常规工作(如计算或IO)时切换执行上下文是不可能的到普通的C函数)。只有在使用Go并发原语时才能切换上下文。当你打开几个chans,或者(这是你的情况),当你明确地告诉调度器切换上下文 - 这是runtime.Gosched
的用途。
因此,简而言之,当一个例程中的执行上下文达到Gosched
调用时,指示调度程序将执行切换到另一个goroutine。在你的情况下,有两个goroutines,main(它代表程序的'main'线程)和另外一个,你用go say
创建的。如果删除Gosched
调用,执行上下文将永远不会从第一个goroutine传输到第2个goroutine,因此对您而言没有“世界”。当存在Gosched
时,调度程序会将每次循环迭代的执行从第一个goroutine转移到第二个goroutine,反之亦然,所以您有'hello'和'world'交错。我们称之为'合作式多任务':goroutines必须明确地将控制权交给其他goroutines。大多数现代操作系统使用的方法称为“抢先式多任务”:执行线程不关心控制传输;调度程序将执行上下文透明地切换给它们。经常使用合作的方法来实现'绿色线程',也就是逻辑并发协程,它们不会将1:1映射到操作系统线程 - 这就是Go运行时及其goroutines的实现方式。
更新
我已经提到GOMAXPROCS环境变量,但没有说明它是什么。现在是解决这个问题的时候了。
当此变量设置为正数N
时,Go运行时将能够创建最多N
本机线程,其上将调度所有绿色线程。本机线程是由操作系统创建的一种线程(Windows线程,pthreads等)。这意味着如果N
大于1,那么goroutines可能会安排在不同的本地线程中执行,因此可以并行运行(至少取决于您的计算机功能:如果您的系统基于多核处理器,这些线程可能会真正并行;如果您的处理器具有单核,那么在OS线程中实现的抢先式多任务将创建并行执行的可见性)。
可以使用runtime.GOMAXPROCS()
函数来设置GOMAXPROCS变量,而不是预先设置环境变量。使用这样的事情在你的程序,而不是当前main
的:
func main() {
runtime.GOMAXPROCS(2)
go say("world")
say("hello")
}
在这种情况下,你可以看到有趣的结果。您可能会得到打印错误交错的“hello”和“world”行,例如
hello
hello
world
hello
world
world
...
如果够程定于单独操作系统的线程会发生这种情况。实际上,抢先式多任务处理(或多核系统情况下的并行处理)如何实现:线程是并行的,并且它们的组合输出是不确定的。顺便说一句,你可以离开或删除Gosched
通话,它似乎没有任何影响时,GOMAXPROCS是大于1.
以下是我所得到的与runtime.GOMAXPROCS
调用的几个运行的程序。
hyperplex /tmp % go run test.go
hello
hello
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
hello
hello
hello
hello
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
请参阅,有时输出很漂亮,有时不是。非决定论在行动:)
另一个更新
看起来像在围棋编译器Go运行时力的新版本够程产生不仅并发原语的用法,但对OS的系统调用了。这意味着执行上下文可以在IO函数调用中在goroutine之间切换。因此,在最近的Go编译器中,即使当GOMAXPROCS未设置或设置为1时,也可以观察到不确定行为。
伟大的工作!但我没有遇到这个问题下去1.0.3,更奇怪。 – MrROY
这是真的。我刚刚检查了1.0.3版本,是的,这种行为没有出现:即使使用GOMAXPROCS == 1,程序的工作方式就好像GOMAXPROCS> = 2。似乎在1.0.3中调度器已经调整。 –
谢谢!我希望我能两次赞扬这个! –