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

报告相同问题?

悬赏问题

  • ¥15 孟德尔随机化结果不一致
  • ¥15 深度学习残差模块模型
  • ¥50 怎么判断同步时序逻辑电路和异步时序逻辑电路
  • ¥15 差动电流二次谐波的含量Matlab计算
  • ¥15 Can/caned 总线错误问题,错误显示控制器要发1,结果总线检测到0
  • ¥15 C#如何调用串口数据
  • ¥15 MATLAB与单片机串口通信
  • ¥15 L76k模块的GPS的使用
  • ¥15 请帮我看一看数电项目如何设计
  • ¥23 (标签-bug|关键词-密码错误加密)