Search code examples
goconcurrencychannelgo-scheduler

go buffered channel (capacity 4) accepts 5 without blocking


I have the go code, that basically runs two go routines concurrently. One of them sends 10 int numbers from (1-10) to the buffered channel "ch"(capacity is 4), and another go routine reads values from channel with the help of for range loop

  package main

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

  func doSm(ch chan int, wg *sync.WaitGroup) {
      defer wg.Done()

      for i := 1; i <= 10; i++ {
          fmt.Println("sending", i)
          ch <- i
          fmt.Println("sent", i)
      }

      close(ch)
  }

  func doSm2(ch chan int, wg *sync.WaitGroup) {
      defer wg.Done()

      time.Sleep(5 * time.Second)
      for v := range ch {
          fmt.Println("result:", v)
      }
  }

  func main() {
      runtime.GOMAXPROCS(1)
      var wg sync.WaitGroup
      ch := make(chan int, 4)

      wg.Add(2)
      go doSm(ch, &wg)
      go doSm2(ch, &wg)
      wg.Wait()
  }

The issue can be found in the output below.

doSm() sends 4 int through channel and go scheduler blocks the go routine until doSm2() reads those 4 values from channel. After that, the buffer is empty and doSm() sends 1 int, and doSm2() instantly reads it. Now, buffer is empty again and ready to send 4 values. However, doSm() somehow sends 5 values (6, 7, 8, 9, 10) in spite of its capacity.

sending 1
sent 1
sending 2
sent 2
sending 3
sent 3
sending 4
sent 4
sending 5
result: 1
result: 2
result: 3
result: 4
result: 5
sent 5
sending 6
sent 6
sending 7
sent 7
sending 8
sent 8
sending 9
sent 9
sending 10
sent 10
result: 6
result: 7
result: 8
result: 9
result: 10

Any idea why is it happening? Or am I missing something?


Solution

  • An example explanation.

    result: 5
    sent 5
    sending 6
    sent 6
    sending 7
    sent 7
    sending 8
    sent 8
    sending 9
    sent 9
    sending 10
    sent 10
    result: 6
    

    result 5 is printed, then we see sent 6-7-8-9-10, then we see result 6 printed. This does not mean the values 6-7-8-9-10 are all in the channel's buffer (obviously they aren't). The value 6 is already received from the channel, but the subsequent fmt.Println() line is not yet executed. But since 6 is received, there are only 3 numbers in the buffer and so 10 can be sent on the channel, as can be seen in the output.