我有一个简单的go程序,其中有两个消费者频道同时从一个制作人那里读取:
package main
import "fmt"
func main() {
producer := make(chan int)
wait := make(chan int)
go func() {
for i := 0; i < 1000; i++ {
producer <- i
}
close(producer)
wait <- 1
}()
go func() {
count := 0
for _ = range producer {
count++
}
fmt.Printf("Consumer 1: %i\n", count)
}()
go func() {
count := 0
for _ = range producer {
count++
}
fmt.Printf("Consumer 2: %i\n", count)
}()
<-wait
}
我期待两个消费者获得相同数量的数据或至少几乎相等。但结果总是如此:
Consumer 1: %!i(int=667)
Consumer 2: %!i(int=333)
多次运行之间保持不变。任何人都可以为我解释这种行为吗?
环境:
go version go1.4.2 darwin/amd64
GOARCH="amd64"
GOBIN=""
GOCHAR="6"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/usr/local/go/:/Users/victor/Dropbox/projects/go/"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.4.2/libexec"
GOTOOLDIR="/usr/local/Cellar/go/1.4.2/libexec/pkg/tool/darwin_amd64"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fno-common"
CXX="clang++"
CGO_ENABLED="1"
答案 0 :(得分:2)
Go Go编程语言规范中未定义Go goroutine调度算法。它未定义,因此,它的实现和版本依赖。 Go的当前实现使用协作调度方案。协作调度方案依赖于goroutine来执行不时向调度器屈服的操作。调度程序代码在包运行时中。
该程序取决于Go通道操作。
该程序还取决于硬件(例如,CPU数量),操作系统和其他正在运行的程序。
您的代码不应期望从goroutine调度进行特定分发。
将1.4默认设置为GOMAXPROCS(1);对于Go 1.5及更高版本,它默认为NumCPU()。
我已经修改了你的程序以修复错误(附加的等待语句),显示诊断信息,并在某些点上屈服于调度程序(Gosched())。现在,该程序在Go 1.6(开发提示),NumCPU()== 8,GOMAXPROCS(8)和Go Playround,Go 1.5.1,NumCPU()== 1,GOMAXPROCS(1)上密切再现您的结果。在紧密循环中的某些点而不是在其他点处向goroutine调度程序屈服,是重现结果的关键。
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println(runtime.Version())
fmt.Println(runtime.NumCPU())
fmt.Println(runtime.GOMAXPROCS(0))
producer := make(chan int, 100)
wait := make(chan int, 100)
go func() {
for i := 0; i < 1000; i++ {
producer <- i
runtime.Gosched()
}
close(producer)
wait <- 1
}()
go func() {
count := 0
for _ = range producer {
count++
}
fmt.Printf("Consumer 1: %d\n", count)
wait <- 1
}()
go func() {
count := 0
for _ = range producer {
count++
runtime.Gosched()
}
fmt.Printf("Consumer 2: %d\n", count)
wait <- 1
}()
<-wait
<-wait
<-wait
}
屈服(Gosched()):
> go run yield.go
8
8
Consumer 1: 668
Consumer 2: 332
> go run yield.go
devel +54b4b94 Sat Feb 6 23:33:23 2016 +0000
8
8
Consumer 2: 336
Consumer 1: 664
> go run yield.go
devel +54b4b94 Sat Feb 6 23:33:23 2016 +0000
8
8
Consumer 2: 333
Consumer 1: 667
>
playground:https://play.golang.org/p/griwLmsPDf
go1.5.1
1
1
Consumer 1: 674
Consumer 2: 326
go1.5.1
1
1
Consumer 1: 674
Consumer 2: 326
go1.5.1
1
1
Consumer 1: 674
Consumer 2: 326
为了比较,不屈服:
> go run noyield.go
devel +54b4b94 Sat Feb 6 23:33:23 2016 +0000
8
8
Consumer 1: 81
Consumer 2: 919
> go run noyield.go
devel +54b4b94 Sat Feb 6 23:33:23 2016 +0000
8
8
Consumer 1: 123
Consumer 2: 877
> go run noyield.go
devel +54b4b94 Sat Feb 6 23:33:23 2016 +0000
8
8
Consumer 1: 81
Consumer 2: 919
> go run noyield.go
devel +54b4b94 Sat Feb 6 23:33:23 2016 +0000
8
8
Consumer 2: 673
Consumer 1: 327
playground:https://play.golang.org/p/2KV1B04VUJ
go1.5.1
1
1
Consumer 1: 100
Consumer 2: 900
go1.5.1
1
1
Consumer 1: 100
Consumer 2: 900
go1.5.1
1
1
Consumer 1: 100
Consumer 2: 900