为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕尼黑的夜晚无繁华 2021-12-07 18:59:22
我有一个简单的 go 程序,它有 2 个消费者通道同时从一个生产者读取,如下所示:package mainimport "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/amd64GOARCH="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"
查看完整描述

1 回答

?
料青山看我应如是

TA贡献1772条经验 获得超8个赞

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 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

>

游乐场: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

游乐场: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


查看完整回答
反对 回复 2021-12-07
  • 1 回答
  • 0 关注
  • 142 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信