关于Go协程调度的一个小问题

根据官方文档的描述,用Go语言编写的程序在运行的时候,对协程的调度是不确定的。也就是说某一个时间里,具体哪个协程在运行是不确定的。但是下面出现了一个反例:

package main

import (
	"fmt"
)

func main() {
	c := make(chan string)
	done := make(chan struct{})
	go feed("cat", c, done)
	go feed("dog", c, done)
	c <- "fish"
	<-done
}

func feed(name string, ch chan string, done chan struct{}) {
	meat := <-ch
	fmt.Println(name, "eat", meat)
	done <- struct{}{}
}

运行上面这段代码输出的总是cat eat fish。也就是说,每次都是cat那个协程可以拿到通道中的数据。而不是catdog随机从通道中拿到数据。这个现象和官方的描述是有出入的。但是对这段代码稍作改动,每次运行的时候,出现的结果就会是随机的,且看下面这段代码:

package main

import (
	"fmt"
	"runtime"
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU() * 2)
}

func main() {
	c := make(chan string)
	done := make(chan struct{})
	go feed("cat", c, done)
	go feed("dog", c, done)
	c <- "fish"
	<-done
}

func feed(name string, ch chan string, done chan struct{}) {
	meat := <-ch
	fmt.Println(name, "eat", meat)
	done <- struct{}{}
}

这段代码在运行的时候,基本上dog eat fishcat eat fish是随机出现的。符合官方对协程调度的描述。

分析

第二段代码中,增加了runtime.GOMAXPROCS,设置了最大的Process的数量。换句话说,就是允许多个 Process来调度协程。这个时候,catdog两个协程就是随机从通道中获取到数据。因此,在默认协程数量为1的时候,先启动的协程似乎优先拥有时间片,但是这还需要阅读标准库中的源代码进行确认。

Xiang Chao 13 January 2015
blog comments powered by Disqus
Fork me on GitHub