5

介绍:

我刚刚开始学习 Go 语言,并且已经上过关于并发的课程。

我为自己发明了一个小任务来尝试实现我所学到的关于关闭 goroutines 的知识。

问题:

如果我们关闭通道,case它将始终在select语句中被选中,这是向所有 goroutine 广播取消信号的好方法。

下面我有 2 个 goroutine 和一个quit永远不会收到的频道。

Playground上的代码超时。

default如果我在 goroutines 中注释掉部分,那么quit就会收到信号。

问题:

尽管我正在尝试,但我真的不明白为什么会发生这种情况以及如何解决它。
有人可以解释一下是什么问题并就如何解决它提供一些建议吗?

package main

import (
    "fmt"
    "sync"
    "time"
)

func positive_numbers(quit chan struct{}, wg *sync.WaitGroup) {
    defer wg.Done()
    i := 1
    for {
        select {
        case <-quit:
            fmt.Println("[+]Quiting...")
            return
        default:
            fmt.Printf("%v ", i)
            i++
        }
    }
}

func negative_numbers(quit chan struct{}, wg *sync.WaitGroup) {
    defer wg.Done()
    i := -1
    for {
        select {
        case <-quit:
            fmt.Println("[-]Quiting...")
            return
        default:
            fmt.Printf("%v ", i)
            i--
        }
    }
}

func main() {
    quit := make(chan struct{})

    wg := sync.WaitGroup{} // so we can wait for all goroutines to finish
    wg.Add(2)

    go positive_numbers(quit, &wg)
    go negative_numbers(quit, &wg)

    go func(quit chan struct{}) {
        defer close(quit)
        time.Sleep(1 * time.Second)
    }(quit)

    wg.Wait()
}
4

1 回答 1

6

此代码在现实生活中可以正常工作;它只是与操场的“假时间”不兼容,因为positive_numbers并且negative_numbers不会阻塞,并且运行时无法决定每个迭代在Sleep到期之前运行多少次(基本上,打印需要零模拟挂钟时间,所以他们尝试产生无限输出,并且您在完全没有提前挂钟时间的情况下达到了操场 CPU 使用限制)。在真正的机器上,你只能在有限的时间内打印有限的输出,你会得到合理的行为。

在操场上,如果你在每次打印后添加类似的东西time.Sleep(time.Millisecond),你就会强制假时钟向前移动,程序也会终止。

于 2021-08-13T07:29:36.850 回答