Home > database >  Go Channel writes data beyond it's capacity
Go Channel writes data beyond it's capacity

Time:01-03

package main

import (
    "fmt"
)

func write(ch chan int) {
    for i := 0; i < 5; i   {
        fmt.Println("avaliable", i)
        ch <- i
        fmt.Println("successfully wrote", i, "to ch")
    }

    close(ch)
}

func main() {
    ch := make(chan int)

    go write(ch)

    for v := range ch {
        fmt.Println("read value", v, "from ch")
    }
}

Output

avaliable 0
successfully wrote 0 to ch
avaliable 1
read value 0 from ch
read value 1 from ch
successfully wrote 1 to ch
avaliable 2
successfully wrote 2 to ch
avaliable 3
read value 2 from ch
read value 3 from ch
successfully wrote 3 to ch
avaliable 4
successfully wrote 4 to ch
read value 4 from ch

Since this is a unbuffered channel it should have blocked as soon as data is written into it until another goroutine reads from the same channel. But it accepts data beyond it's capacity.

Intended Behavior

package main

import (
    "fmt"
    "time"
)

func write(ch chan int) {
    for i := 0; i < 5; i   {
        fmt.Println("avaliable", i)
        ch <- i
        fmt.Println("successfully wrote", i, "to ch")
    }

    close(ch)
}

func main() {
    ch := make(chan int)

    go write(ch)

    time.Sleep(time.Second)

    for v := range ch {
        fmt.Println("read value", v, "from ch")

        time.Sleep(time.Second)
    }
}

Output

avaliable 0
read value 0 from ch
successfully wrote 0 to ch
avaliable 1
read value 1 from ch
successfully wrote 1 to ch
avaliable 2
read value 2 from ch
successfully wrote 2 to ch
avaliable 3
read value 3 from ch
successfully wrote 3 to ch
avaliable 4
read value 4 from ch
successfully wrote 4 to ch

If some timers are placed throughout the code so that the main goroutine is blocked before each iteration it works as expected.

CodePudding user response:

You can't infer anything from your output, because there is no ordering guarantee between the "read value" and "successfully wrote" Printfs executing. (There is one between the "available" Printf, which occurs before the channel send, and the "read value" Printf, which occurs after the corresponding channel receive, and you can see that that ordering is never violated in your output).

The channel isn't buffering anything, because it has no buffer; it's just that the two different goroutines run in an indeterminate order after the channel send completes. Sometimes the sending side gets to go first and prints the "successfully wrote" message, and sometimes the receiving side gets to go first and prints the "read value" message. Neither one ever "gets ahead" by more than one value, because they're still fully synchronized on the channel send; they're just racing to print their status messages immediately after.

When you add the Sleep calls to main, it just so happens to make it so that the goroutine running write will always be blocked on waiting to send the next value, while the one running main blocks on the call to Sleep. When the timer expires, the main goroutine wakes up, and immediately finds that it has something waiting for it on the channel, grabs it, and goes back to sleep, and then the write goroutine gets woken up. By slowing things down you've gotten the scheduler to run things in a consistent order (although it's still partly a matter of luck); without the sleeps, with everything running as fast as it can, the result is apparently random.

CodePudding user response:

If that were the case, the output of the "sleeping" version would be even more chaotic.

You cannot expect a coherent output from two independent threads even if they are synchronized at some point.

  • Related