【问题标题】:In a Goroutines with select case and default, once channel closed, default should not get executed在具有选择案例和默认值的 Goroutines 中,一旦通道关闭,默认值不应被执行
【发布时间】:2018-07-09 16:52:53
【问题描述】:

我试图了解默认情况下选择块中关闭通道的行为,但对以下输出感到困惑。 这里调用了 50 个 goroutine 并关闭了完成通道。

func testClosedChannelBehavior() {
    const n = 50
    finish := make(chan bool)
    var done sync.WaitGroup
    for i := 0; i < n; i++ {
        done.Add(1)
        go func(x int) {
            select {
            case <-time.After(1 * time.Hour):
            case <-finish:
                fmt.Printf("received finish %d\n", x)
            default:
            fmt.Printf("I didnt wait %d\n", x)
            }
            done.Done()
        }(i)
    }
    t0 := time.Now()
    close(finish) 
    fmt.Println("finish closed")
    done.Wait() 
    fmt.Printf("Waited %v for %d goroutines to stop\n",     time.Since(t0), n)
}

我预计一旦任何 goroutine 打印“received finish”,默认情况不应被任何其他 goroutine 执行,即“我没有等待”不应该被打印。 但输出不一致。有时它的行为符合预期,但在多次运行时,我会看到如下意外输出: =====output====== I didnt wait 0 received finish 7 finish closed received finish 13 received finish 10 received finish 32 received finish 5 received finish 14 received finish 33 received finish 42 received finish 11 received finish 4 received finish 23 received finish 44 received finish 49 received finish 15 received finish 24 received finish 31 received finish 16 received finish 40 received finish 41 received finish 6 received finish 26 I didnt wait 1 received finish 19 received finish 8 received finish 43 received finish 29 received finish 20 received finish 46 received finish 12 received finish 36 received finish 47 received finish 37 received finish 35 received finish 30 received finish 39 received finish 22 received finish 28 I didnt wait 2 received finish 17 received finish 45 I didnt wait 9 received finish 48 received finish 34 I didnt wait 3 received finish 25 received finish 38 received finish 27 received finish 18 received finish 21 Waited 394.999µs for 50 goroutines to stop

我正在经历 this link 期望 close(finish) 会向其他人发出信号,那些仍在等待的人,表现得一样。

【问题讨论】:

    标签: go


    【解决方案1】:

    对 fmt.Printf 的调用涉及系统调用。系统调用会自动导致该 goroutine 被重新调度,因为它必须等待操作系统完成该系统调用。这意味着其中一些 goroutine 很有可能运行 select 语句并选择默认情况,但 还没有 打印到控制台。

    编辑:此外,如果您在具有多个线程的系统上运行此程序,则 go 运行时默认情况下将运行多个 go 例程并行(匹配操作系统线程的数量),这意味着其中一些 goroutine 可能在通道关闭的同时执行,并在主 goroutine 中发生通道关闭之前到达 select 语句。

    如果您添加一个同步通道以确保通道关闭操作发生在 select 在任何 goroutine 中发生之前,它会按预期工作:

    https://play.golang.org/p/XtUYaihKgRT

    func testClosedChannelBehavior() {
        const n = 50
        finish := make(chan bool)
        proceed := make(chan struct{})
        var done sync.WaitGroup
        for i := 0; i < n; i++ {
            done.Add(1)
            go func(x int) {
                <-proceed
                select {
                case <-time.After(1 * time.Hour):
                case <-finish:
                    fmt.Printf("received finish %d\n", x)
                default:
                    fmt.Printf("I didnt wait %d\n", x)
                }
                done.Done()
            }(i)
        }
        t0 := time.Now()
        close(finish)
        fmt.Println("finish closed")
        close(proceed)
        done.Wait()
        fmt.Printf("Waited %v for %d goroutines to stop\n", time.Since(t0), n)
    }
    

    【讨论】:

    • 感谢@Kaedys 的快速回复。会接受这个作为答案。同步频道是一个干净的修复:)...
    猜你喜欢
    • 1970-01-01
    • 2015-02-05
    • 2021-05-17
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 2018-07-24
    • 2017-09-02
    相关资源
    最近更新 更多