Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infinite loop with zeros while trying to achieve deadlock

Tags:

go

channel

The following code keeps printing 0.

package main

import (
    "fmt"
)

func main() {

    c := make(chan int)

    go func() {
        for i := 0; i < 10; i++ {
            c <- i // INPUT
        }
        close(c)
    }()

    for {
        fmt.Print(<-c) // RECEIVER; OUTPUT
    }
}

From my understanding, it should print 0 to 9 and then go on an infinite loop. Why does it keep printing zero?

like image 286
Suhail Gupta Avatar asked Sep 30 '17 09:09

Suhail Gupta


1 Answers

Your understanding is (almost) correct, except that the infinite loop in the main goroutine will not block but will receive and print relentlessly.

The goroutine you start sends the numbers 0, 1, ... 9 on the channel, then closes it. And receiving from a closed channel does not block, on the contrary, it can proceed immediately, and it yields the zero value of the element type of the channel, which is 0 for the type int. This is stated in Spec: Receive operator:

Receiving from a nil channel blocks forever. A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.

So you see exactly what you should. First it prints the numbers 0..9, then keeps printing 0 very fast (without any delay), so probably you don't even notice the initial 0..9 numbers.

Slightly modifying your example so that the loop in the main goroutine exits after 15 iterations will immediately show what happens:

c := make(chan int)
go func() {
    for i := 0; i < 10; i++ {
        c <- i // INPUT
    }
    close(c)
}()

for i := 0; i < 15; i++ {
    fmt.Print(<-c) // RECEIVER; OUTPUT
}

Output (try it on the Go Playground):

012345678900000

If your goal is to quit once all sent numbers have been processed (printed), use the for range on the channel:

for i := range c {
    fmt.Print(i) // RECEIVER; OUTPUT
}

If your goal is to block until new numbers are available once those 10 numbers are processed, then don't close the channel. That way the next receive operation will block. In this case both your original loop and the for range loop would work, but for range is better as that always exits if / when the channel is closed.

Check out this question and memorize the channel axioms: How does a non initialized channel behave?

like image 125
icza Avatar answered Sep 30 '22 19:09

icza