Scott Frazer
Scott Frazer

Reputation: 2185

Why does this Go program hang?

I'm setting up a chain of three goroutines, each with an input an output channel. The goroutines will read from the input channel until it's closed, increment the value, send it to the output channel. However, the program below deadlocks with this output:

goroutine 'one': 1
goroutine 'two': 2
goroutine 'three': 3
goroutine 'one': 10
goroutine 'two': 11
goroutine 'one': 100
fatal error: all goroutines are asleep - deadlock!

Code:

package main

import (
  "fmt"
)

func int_channel(id string, i chan int, o chan int) {
  defer close(o)

  for x := range i {
    fmt.Printf("goroutine '%s': %d\n", id, x)
    o <- x + 1
  }

  fmt.Println("done")
}

func main() {
  c0 := make(chan int)
  c1 := make(chan int)
  c2 := make(chan int)
  c3 := make(chan int)

  go int_channel("one", c0, c1)
  go int_channel("two", c1, c2)
  go int_channel("three", c2, c3)

  c0 <- 1
  c0 <- 10
  c0 <- 100
  c0 <- 1000
  c0 <- 10000
  c0 <- 100000
  close(c0)

  fmt.Println("Sent all numbers to c0")

  for x := range c3 {
    fmt.Printf("out: %d\n", x)
  }
}

Upvotes: 5

Views: 3940

Answers (2)

ain
ain

Reputation: 22749

It hangs because the loop which reads from the output channel is never reached, thus the channels are not "emptyed" and once each channe has a value written into it no progress can be made and program hangs. To fix it write to the input in another goroutine, ie

func main() {
  c0 := make(chan int)
  c1 := make(chan int)
  c2 := make(chan int)
  c3 := make(chan int)

  go int_channel("one", c0, c1)
  go int_channel("two", c1, c2)
  go int_channel("three", c2, c3)

  go func(){
    c0 <- 1
    c0 <- 10
    c0 <- 100
    c0 <- 1000
    c0 <- 10000
    c0 <- 100000
    fmt.Println("Sent all numbers to c0")
    close(c0)
  }()


  for x := range c3 {
    fmt.Printf("out: %d\n", x)
  }
}

IOW, when the line c0 <- 1 is executed, the value flows throught all three cannels and ends up in c3, but since the reader loop is not reached yet, it just "sits in there". Then the line c0 <- 10 is executed, and this value ends up in c2 because it can't be written into c3 - the previous value is still in there, blocking the write. And thus when the line c0 <- 100 is executed, all channels are full and no further progress can be made.

Upvotes: 6

abhink
abhink

Reputation: 9116

You are not reading from c3 in a timely manner. You are sending way too many values into c0. So the communication between channels happens like this:

send 1 on c0 => recv 1 on c1 => send 2 on c2 =>
        recv 2 on c2 => send 3 on c3 => recv 3 on c3

send 10 on c0 => recv 10 on c1 => send 11 on c2 =>
        recv 11 on c2 => send 12 on c3 -- // c3 still hasn't been read
                                          // from and so send
                                          // operation blocks here.

c0, c1 and c2 continue to receive until all their sends block eventually. You can refer to @ain's answer below for a solution.

Upvotes: 2

Related Questions