This question already has an answer here:
I've got a goroutine that's playing some audio infinitely play()
. In order to keep the play()
alive, I've got the calling function running an infinite for loop afterward.
The unexpected thing is that a barebones loop does not seem to let the function play infinitely and I'm at a loss as to why. However, if I add a simple time.Sleep(time.Second)
into the body of the for-loop, it runs seemingly infinitely. Any idea as to why?
To visualize:
func PlaysForAFewSeconds() {
go play()
for {
}
}
^plays for a few seconds but never breaks out
func PlaysForever() {
go play()
for {
time.Sleep(time.Second)
}
}
^ plays forever.
I'm guessing that this has something to do with how play()
is implemented but I'm hoping this is a common enough problem that someone recognizes this symptom. Thanks.
</div>
The assembly that for { }
generates is jmp self
, where self
is the location of the jmp
instruction. In other words, the CPU will just keep running jmp
instructions as fast as it can. The CPU can run n instructions per second, and it doesn't matter if this is a useless jmp
or an actually useful instruction.
This is known as a "busy wait" or "spin lock", and this behaviour is not specific to Go. Most (all?) programming languages behave like this.
There are some uses for such loops, but in Go they can often be replaced with channels:
// 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
}
}
Reading from a channel (<-wait
) is blocking and doesn't use any CPU. I used an empty anonymous struct, which looks a bit ugly, as that doesn't allocate any memory.