为什么 goroutine 的执行顺序在运行之间是相同的

我有一个简单的 go 程序,它有 2 个消费者通道同时从一个生产者读取,如下所示:


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"


慕尼黑的夜晚无繁华
浏览 143回答 1
1回答

料青山看我应如是

Go 编程语言规范中没有定义 Go 协程调度算法。它是未定义的,因此它依赖于实现和版本。Go 的当前实现使用协作调度方案。协作调度方案依赖于 goroutines 来执行不时让给调度程序的操作。调度程序代码在包运行时中。该程序依赖于 Go 通道操作。该程序还依赖于硬件(例如,CPU 数量)、操作系统和其他正在运行的程序。您的代码不应期望来自 goroutine 调度的特定分布。Go 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 mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "runtime")func main() {&nbsp; &nbsp; fmt.Println(runtime.Version())&nbsp; &nbsp; fmt.Println(runtime.NumCPU())&nbsp; &nbsp; fmt.Println(runtime.GOMAXPROCS(0))&nbsp; &nbsp; producer := make(chan int, 100)&nbsp; &nbsp; wait := make(chan int, 100)&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; for i := 0; i < 1000; i++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; producer <- i&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; runtime.Gosched()&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; close(producer)&nbsp; &nbsp; &nbsp; &nbsp; wait <- 1&nbsp; &nbsp; }()&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; count := 0&nbsp; &nbsp; &nbsp; &nbsp; for _ = range producer {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count++&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("Consumer 1: %d\n", count)&nbsp; &nbsp; &nbsp; &nbsp; wait <- 1&nbsp; &nbsp; }()&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; count := 0&nbsp; &nbsp; &nbsp; &nbsp; for _ = range producer {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count++&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; runtime.Gosched()&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("Consumer 2: %d\n", count)&nbsp; &nbsp; &nbsp; &nbsp; wait <- 1&nbsp; &nbsp; }()&nbsp; &nbsp; <-wait&nbsp; &nbsp; <-wait&nbsp; &nbsp; <-wait}随着屈服(Gosched()):> go run yield.go88Consumer 1: 668Consumer 2: 332> go run yield.godevel +54b4b94 Sat Feb 6 23:33:23 2016 +000088Consumer 2: 336Consumer 1: 664> go run yield.godevel +54b4b94 Sat Feb 6 23:33:23 2016 +000088Consumer 2: 333Consumer 1: 667>游乐场:https : //play.golang.org/p/griwLmsPDfgo1.5.111Consumer 1: 674Consumer 2: 326go1.5.111Consumer 1: 674Consumer 2: 326go1.5.111Consumer 1: 674Consumer 2: 326为了比较,没有屈服:> go run noyield.godevel +54b4b94 Sat Feb 6 23:33:23 2016 +000088Consumer 1: 81Consumer 2: 919> go run noyield.godevel +54b4b94 Sat Feb 6 23:33:23 2016 +000088Consumer 1: 123Consumer 2: 877> go run noyield.godevel +54b4b94 Sat Feb 6 23:33:23 2016 +000088Consumer 1: 81Consumer 2: 919> go run noyield.godevel +54b4b94 Sat Feb 6 23:33:23 2016 +000088Consumer 2: 673Consumer 1: 327游乐场:https : //play.golang.org/p/2KV1B04VUJgo1.5.111Consumer 1: 100Consumer 2: 900go1.5.111Consumer 1: 100Consumer 2: 900go1.5.111Consumer 1: 100Consumer 2: 900
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go