【问题标题】:Printing to stdout causes blocked goroutine to run?打印到标准输出会导致阻塞的 goroutine 运行?
【发布时间】:2012-04-26 09:32:13
【问题描述】:

作为一个愚蠢的基本线程练习,我一直在尝试在 golang 中实现 sleeping barber problem。有了频道,这应该很容易,但我遇到了一个 heisenbug。也就是说,当我尝试诊断它时,问题就消失了!

考虑以下问题。 main() 函数将整数(或“客户”)推送到 shop 通道。 barber() 读取shop 频道以剪掉“客户”的头发。如果我将fmt.Print 语句插入customer() 函数,程序将按预期运行。否则,barber() 绝不会剪别人的头发。

package main

import "fmt"

func customer(id int, shop chan<- int) {
    // Enter shop if seats available, otherwise leave
    // fmt.Println("Uncomment this line and the program works")
    if len(shop) < cap(shop) {
        shop <- id
    }
}

func barber(shop <-chan int) {
    // Cut hair of anyone who enters the shop
    for {
        fmt.Println("Barber cuts hair of customer", <-shop)
    }
}

func main() {
    shop := make(chan int, 5) // five seats available
    go barber(shop)
    for i := 0; ; i++ {
        customer(i, shop)
    }
}

知道发生了什么吗?

【问题讨论】:

    标签: concurrency go goroutine


    【解决方案1】:

    在 main 的开头添加runtime.GOMAXPROCS(2) 可以解决这个问题吗?

    【讨论】:

    • @MattJoiner 你能详细说明一下吗?
    • 提高并行度只会减少并发错误的出现。他们还在那里。无论 GOMAXPROCS 的值如何,正确的代码都应该可以工作。也许观看 Rob Pike 的 Concurrency is not Parallelism 演讲可能会让这一点更清楚。
    【解决方案2】:

    问题在于 Go 的调度器的实现方式。当前的 goroutine 只有在进行系统调用或阻塞通道操作时才能让步给其他 goroutine。 fmt.Println 进行系统调用,让 goroutine 有机会让步。否则它没有。

    在实践中这通常并不重要,但对于像这样的小问题,它有时会突然出现。

    此外,在通道上进行非阻塞发送的一种更惯用、更简洁的方式是:

    func customer(id int, shop chan<- int) {
        // Enter shop if seats available, otherwise leave
        select {
        case shop <- id:
        default:
        }
    }
    

    按照您的操作方式,客户最终可能会在理发店外等待,因为当您实际发送邮件时,len(shop) 可能已经改变。

    【讨论】:

    • 如果 go 运行时只使用一个线程,你的答案成立。通过像lazy1 说的运行时调用或设置环境变量来设置 GOMAXPROCS 也将允许任何 goroutine 在单独的线程上与其他 goroutine 并行运行。可能值得扩展您的答案以反映 go 的运行时如何在可用线程上多路复用 goroutine。
    猜你喜欢
    • 2013-01-19
    • 2010-11-19
    • 1970-01-01
    • 2013-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-29
    相关资源
    最近更新 更多