带有活动 goroutine 数量的奇怪输出

我试图通过运行小程序来学习频道。我不明白下面的程序,因为它给出了一个奇怪的输出:


func squares(c chan int) {

    for i := 0; i < 4; i++ {

        num := <-c

        fmt.Println(num * num)

    }

}


func main() {

    c := make(chan int, 3)

    go squares(c)


    c <- 1

    c <- 2

    c <- 3

    c <- 4


    fmt.Println(runtime.NumGoroutine())

    time.Sleep(time.Second)

    fmt.Println(runtime.NumGoroutine())

}

在执行这个程序时,我看到第一次它打印活动 goroutine 的数量为 2。然后一秒钟后,它的输出为 1。这真的很奇怪。


看了几篇博客,没看懂。那么,在 goroutine 停止工作的那一秒内,究竟发生了什么变化?


绝地无双
浏览 116回答 3
3回答

慕的地10843

无法保证 goroutine 的顺序。根据您的观察,看起来在写入通道后,主 goroutine 继续并将活动的 goroutine 打印为两个,然后squaresgoroutine 运行并完成。您也有可能从第一次调用中得到 1 来获取 goroutine 的数量。如果squaresgoroutine 在主 goroutine 写入通道后立即运行,就会发生这种情况。您可以通过在写入通道之后但在获取 goroutine 数量之前添加睡眠调用来强制执行此操作。

潇潇雨雨

您的第二个 goroutine 接收 4 个值,打印它们并squares几乎立即退出(返回时)。你很幸运,它在第一次调用时仍在运行NumGoRoutines。顺便说一句,我建议您阅读有关该语言的信息,而不是通过实验来学习。仅仅因为某些东西在您尝试时有效,并不意味着它是正确的方法并且总是有效的,尤其是在涉及并发的情况下。或者,使用竞争检测器显示任何数据竞争。

jeck猫

让我强制执行排序,这样你甚至不需要计算 goroutine 的数量,试试这个:package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "runtime"&nbsp; &nbsp; "sync")func main() {&nbsp; &nbsp; var wg sync.WaitGroup&nbsp; &nbsp; ch := make(chan int)&nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; &nbsp; &nbsp; for n := range ch {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(n)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }()&nbsp; &nbsp; ch <- 1&nbsp; &nbsp; ch <- 2&nbsp; &nbsp; ch <- 3&nbsp; &nbsp; ch <- 4&nbsp; &nbsp; fmt.Println("NumGoroutine =", runtime.NumGoroutine()) // 2&nbsp; &nbsp; close(ch)&nbsp; &nbsp; wg.Wait()&nbsp; &nbsp; fmt.Println("NumGoroutine =", runtime.NumGoroutine()) // 1}输出:123NumGoroutine = 24NumGoroutine = 1笔记:使用无缓冲通道强制写入通道和从通道读取的一对一同步。显式关闭通道以退出for循环。显式使用wg.Done()表示 goroutine 的结束,所以wg.Wait()等待它。让我先展开循环for:func squares(c chan int) {&nbsp; &nbsp; num := <-c&nbsp; &nbsp; fmt.Println(num * num)&nbsp; &nbsp; num = <-c&nbsp; &nbsp; fmt.Println(num * num)&nbsp; &nbsp; num = <-c&nbsp; &nbsp; fmt.Println(num * num)&nbsp; &nbsp; num = <-c&nbsp; &nbsp; fmt.Println(num * num)}func main() {&nbsp; &nbsp; c := make(chan int, 3)&nbsp; &nbsp; go squares(c)&nbsp; &nbsp; c <- 1&nbsp; &nbsp; c <- 2&nbsp; &nbsp; c <- 3&nbsp; &nbsp; c <- 4&nbsp; &nbsp; fmt.Println("NumGoroutine =", runtime.NumGoroutine())&nbsp; &nbsp; time.Sleep(100 * time.Millisecond)&nbsp; &nbsp; fmt.Println("NumGoroutine =", runtime.NumGoroutine())}现在让我们运行它:由于您使用的是 3 的缓冲通道,因此这 3 行运行速度很快(这意味着无需等待通道同步 - 只需附加1, 2, 3到通道的缓冲区):&nbsp; &nbsp; c <- 1&nbsp; &nbsp; c <- 2&nbsp; &nbsp; c <- 3并且根据操作系统和 CPU 负载,消费者 goroutine 可能会全部消耗或不消耗它们,此时c <- 4,我们有两种情况:通道已满:所以maingoroutine 等待通道同步。通道有一个(或多个)空闲位置:因此c <- 4运行速度很快,只是将4通道的缓冲区放入(无需等待通道同步)。现在,在这c <- 4一点之后,我们也有两种情况:另一个 goroutine 使用4并退出,所以我们只有一个 maingoroutine。另一个 goroutine不消耗4,所以我们有两个goroutine。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go