首页 > 解决方案 > 无限循环有 vs 没有 time.Sleep()

问题描述

我有一个 goroutine 可以无限播放一些音频play()。为了保持play()活力,我让调用函数在之后运行了一个无限的 for 循环。

出乎意料的是,准系统循环似乎并没有让函数无限播放,我不知道为什么。但是,如果我time.Sleep(time.Second)在 for 循环的主体中添加一个简单的,它似乎无限运行。知道为什么吗?

可视化:

func PlaysForAFewSeconds() {
    go play()
    for {
    }
}

^播放几秒钟但从未爆发

func PlaysForever() {
    go play()
    for {
        time.Sleep(time.Second)
    }
}

^ 永远播放。

我猜这与play()实现方式有关,但我希望这是一个足够普遍的问题,有人能识别出这种症状。谢谢。

标签: gogoroutine

解决方案


for { }生成的程序集是jmp self,其中selfjmp指令的位置。换句话说,CPU 将jmp尽可能快地保持运行指令。CPU每秒可以运行n条指令,不管这是一条无用jmp的指令还是一条真正有用的指令。

这被称为“忙等待”或“自旋锁”,这种行为并不是 Go 特有的。大多数(全部?)编程语言的行为都是这样的。

这种循环有一些用途,但在 Go 中,它们通常可以用通道替换:

// Simulate a function that takes 1s to complete.
func play(ch chan struct{}) {
    fmt.Println("play")
    time.Sleep(1 * time.Second)
    ch <- struct{}{}
}

func PlaysForAFewSeconds() {
    wait := make(chan struct{})
    go play(wait)
    <-wait
}

func PlaysForever() {
    wait := make(chan struct{})
    for {
        go play(wait)
        <-wait
    }
}

从通道 ( ) 读取<-wait是阻塞的并且不使用任何 CPU。我使用了一个空的匿名结构,它看起来有点难看,因为它没有分配任何内存。


推荐阅读