douganbi7686 2016-02-07 07:17
浏览 6
已采纳

为什么goroutines执行命令在两次运行之间是相同的

I have a simple go program which has 2 consumer channels reading from one producer at the same time like this:

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
", count)
    }()

    go func() {
        count := 0
        for _ = range producer {
            count++
        }
        fmt.Printf("Consumer 2: %i
", count)
    }()
    <-wait
}

I was expecting two consumers get the same number of data or at least nearly equal. However the result is always:

Consumer 1: %!i(int=667)
Consumer 2: %!i(int=333)

It stays the same between multiple runs. Can anyone explain this behavior for me?

Environment:

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"
  • 写回答

1条回答 默认 最新

  • douxiong3234 2016-02-07 07:38
    关注

    The Go goroutine scheduling algorithm is not defined in The Go Programming Language Specification. It's undefined and, therefore, it's implementation and version dependent. Current implementations of Go use a cooperative scheduling scheme. The cooperative scheduling scheme relies on the goroutines to perform operations which yield to the scheduler from time to time. The scheduler code is in package runtime.

    The program is dependent on Go channel operations.

    The program is also dependent on the hardware (for example, number of CPUs), the operating system, and other running programs.

    Your code should not expect a particular distribution from goroutine scheduling.

    Go 1.4 defaults to GOMAXPROCS(1); for Go 1.5 and later, it defaults to NumCPU().

    I've modified your program to fix bugs (additional wait statements), display diagnostic information, and yield to the scheduler (Gosched()) at certain points. Now, the program closely reproduces your results on Go 1.6 (devel tip), NumCPU() == 8, GOMAXPROCS(8) and on Go Playround, Go 1.5.1, NumCPU() == 1, GOMAXPROCS(1). Yielding to the goroutine scheduler at certain points in tight loops, and not at other points, was the key to reproducing your results.

    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
    ", count)
            wait <- 1
        }()
        go func() {
            count := 0
            for _ = range producer {
                count++
                runtime.Gosched()
            }
            fmt.Printf("Consumer 2: %d
    ", count)
            wait <- 1
        }()
        <-wait
        <-wait
        <-wait
    }
    

    With yielding (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
    

    For comparison, without yielding:

    > 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
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥100 角动量包络面如何用MATLAB绘制
  • ¥15 merge函数占用内存过大
  • ¥15 Revit2020下载问题
  • ¥15 使用EMD去噪处理RML2016数据集时候的原理
  • ¥15 神经网络预测均方误差很小 但是图像上看着差别太大
  • ¥15 单片机无法进入HAL_TIM_PWM_PulseFinishedCallback回调函数
  • ¥15 Oracle中如何从clob类型截取特定字符串后面的字符
  • ¥15 想通过pywinauto自动电机应用程序按钮,但是找不到应用程序按钮信息
  • ¥15 如何在炒股软件中,爬到我想看的日k线
  • ¥15 seatunnel 怎么配置Elasticsearch