Go - Channels cont. and Packages

Contemporary Programming Languages - CS2001 - 14 November 2017

o := make(chan int)
o <- 1 // Deadlock here
fmt.Println(<- o)
o := make(chan int, 3)
o <- 1
o <- 2
o <- 3
<- o
<- o
<- o
<- o // Deadlock here
func yell(quiet chan string loud chan string) {
  for {
    q := <- quiet
    loud <- strings.ToUpper(q)
  }
}

func main() {
  s := []string{"hello", "i", "am", "a", "gopher"}
  input := make(chan string)
  output := make(chan string)
  go yell(input, output)
  for _, e := range s {
    input <- e
  }
  for i := 0; i < len(s); i++ {
    fmt.Println(<- output)
  }
}
// deadlocks since yell needs to send output before receiving again. fix below

func main() {
  s := []string{"hello", "i", "am", "a", "gopher"}
  input := make(chan string, len(s)) // now a buffered channel
  output := make(chan string)
  go yell(input, output)
  for _, e := range s {
    input <- e
  }
  for i := 0; i < len(s); i++ {
    fmt.Println(<- output)
  }
}

Word Count Examples

  • Using a channel to send results
  • Using a channel to signal completion

The Close Function

  • Builtin
  • Closes a channel to indicate that nothing else will be send
  • Sending over a closed channel with cause a runtime panic
  • Receiving from a closed channel will give you a zero-value for the channels type
  • Closing channels is not required. It is useful for indicationg that a channel won’t be used again
  • Receiving from a channel
    val, ok := <- myChan
    // ok indicates if the chan has been closed
    
    for element := range myChan {
    // receives from myChan until myChan is closed.
    }
    

Signaling the end of output

func fib(n int, c chan int) {
  c, y := 0, 1
  for i := 0; i < n; i++ {
    c <- x
    x, y := y, x + y
  }
  close(c)
}
func main() {
  c := make(chan int)
  go fib(10, c)
  for i := range c {
    fmt.Println(i)
  }
}

Signaling the end of input

type Coord struct {
  X, Y float64
}
func printDistance(coords chan Coord, done chan bool) {
  for c := range coords {
    fmt.Println("Distance", math.Hypot(c.X, x.Y))
  }
  done <- true
}
func main() {
  c, d := make(chan Coord), make(chan bool)
  for i := 0; i < 3; i++ {
    go printDistance(c, d)
  }
  for i := 0; i < 10; i++ {
    c <- Coord{rand.Float64), rand.Float64()}
  }
  close(c)
  for i := 0; i < 3; i++ {
    <-d
  }
}

Select Statement

  • Waits on multiple channel operations
  • Runs the first communication operation that is ready.
  • If multiple are ready, one is run at random.
func main() {
  c := make(chan int)
  quit := time.After(5 * time.Millisecond) // sends after 5 seconds
  go func() {
    for i := 0; i++ {
      c <- i
    }
  }()
  for {
    select {
    case val := <- c:
      fmt.Println(val)
    case <- quit:
      fmt.Println("quit")
      return
    }
  }
}
func main() {
  nextFib := make(chan int)
  quit := make(chan int)
  go func() {
    for i := 0; i < 10; i++ {
      fmt.Println(<-nextFib)
    }
    quit <- 0
  }()

  x, y := 0
  for {
    select {
    case nextFib <- x:
      x, y := y, x + y
    case <- quit:
      fmt.Println("quit")
      return
    }
  }
}

Packages

  • You can break Go programs into smaller files and create packages.
  • Example layout:
    src/
      mypackage/
        stuff.go // package mypackage
        other.go // package mypackage
      myexec/
        main.go // package main
        helpers.go // package main
    
  • You can build your executable by running:
    • go build myexec
    • go install myexec
    • in either case, you must set the GOPATH environment variable.
  • Everything in the package is accessible within the package itself.
  • Other packages can only access exported items.