GO: Creating and Using Channels

  • Channels in GO allow communication between goroutines.
  • Channels can only transmit data of a specified type.
  • They can be viewed as pipes between goroutines.
Channels in GO allow communication between goroutines.
  • In program channelEx1.go, n is taken from the command line, e.g. go run channelEx1.go 5
/*channelEx1.go*/
package main

import ("fmt"; "os"; "strconv")

var done = make(chan bool)

func routine1(p chan int, n int) {
   for i:=0; i<n; i++{
      p <- i
      fmt.Println("routine 1 sent", i)
      s :=  <- p
      fmt.Println("routine 1 received", s)
   }
   close(p)
   fmt.Println("routine1 ended")
   done <- true
}

func routine2(p chan int) {
   for {
      v, open := <-p
      if !open {
         fmt.Println("routine2 ended")
         return
      }
      fmt.Println("routine 2 received", v)
      p <- v*v
      fmt.Println("routine 2 sent", v*v)
   }
}

func main() {
   n, _ := strconv.Atoi(os.Args[1])
   p := make(chan int)
   go routine1(p, n)
   go routine2(p)
   <-done
   fmt.Println("main program ended")
}
  • A channel p is created on line 35 using the command make(chan int) This channel is bidirectional and can send or receive data of type int only.
  • goroutine1 sends out the numbers 0 … n − 1 and then closes channel p.
  • goroutine2 attempts to receive a value on channel p. It exits if p is closed; otherwise it sends back the square of the received value.
  • done is a channel of type boolean (declared on line 6) used to signal the completion of goroutine1 (line 17).
  • The main program waits for a value to be received on channel done (line 38) and then exits.
  • The numbers and their squares are printed strictly in order. This ordering is imposed by the use of sends and receives on channels.
  • Here is an example execution:
$ go run channelEx1.go 5
routine 1 sent 0
routine 2 received 0
routine 2 sent 0
routine 1 received 0
routine 1 sent 1
routine 2 received 1
routine 2 sent 1
routine 1 received 1
routine 1 sent 2
routine 2 received 2
routine 2 sent 4
routine 1 received 4
routine 1 sent 3
routine 2 received 3
routine 2 sent 9
routine 1 received 9
routine 1 sent 4
routine 2 received 4
routine 2 sent 16
routine 1 received 16
routine1 ended
main program ended

The select Statement and Channels

  • The select statement allows the execution of different actions depending on which channel has sent a message.
  • The program channelEx2.go creates a server and three clients.
The select statement allows different actions to be executed based on where a message came from.
/*channelEx2.go*/
package main

import ("fmt"; "os"; "strconv"; "math/rand"; "time")

func client(c chan int, id int) {
   for {//infinite loop:
         //generates a random number between 0 and 9:
      num := rand.Intn(10)
         //sends it out on its channel:
      c <- num
      fmt.Printf("client %d sent %d\n", id, num)
         //wait for response from the server:
      val :=  <- c
      fmt.Printf("client %d rxed %d\n", id, val)
         //2 sec sleep to comfortably view progress:
      time.Sleep(2*time.Second)
   }
}

func server(ch0 chan int, ch1 chan int, ch2 chan int) {
   var n int
   for {//infinite loop:
      select {//action based on channel that sent message
      case n = <- ch0:
         fmt.Printf("Server rxed %3d from client 0\n", n)
         ch0 <- n*2
      case n = <- ch1:
         fmt.Printf("Server rxed %3d from client 1\n", n)
         ch1 <- n*3
      case n = <- ch2:
         fmt.Printf("Server rxed %3d from client 2\n", n)
         ch2 <- n*4
      }
   }
}

func main() {
   seed, _ := strconv.Atoi(os.Args[1])
   rand.Seed(int64(seed))
      //Three channels are created:
   ch0 := make(chan int); ch1 := make(chan int);
   ch2 := make(chan int)
      //server goroutine created and passed the channels:
   go server(ch0,ch1,ch2)
      //Each client goroutine is passed a channel & id
      //(id for exposition only):
   go client(ch0,0); go client(ch1,1); go client(ch2,2)
      //servers & clients keep executing infinite loops
      //until "Enter" is pressed:
   fmt.Scanln()
      //"Enter" pressed: program & goroutines termininated
   fmt.Println("main program ended")
}

Here is a typical output:

client 0 sent 6
Server rxed   6 from client 0
Server rxed   3 from client 2
client 2 sent 3
client 2 rxed 12
Server rxed   0 from client 1
client 1 sent 0
client 1 rxed 0
client 0 rxed 12
client 1 sent 3
Server rxed   3 from client 1
Server rxed   2 from client 2
client 2 sent 2
client 2 rxed 8
Server rxed   8 from client 0
client 1 rxed 9
client 0 sent 8
client 0 rxed 16
client 0 sent 2
Server rxed   2 from client 0
Server rxed   2 from client 1
client 1 sent 2
client 1 rxed 6
Server rxed   2 from client 2
client 2 sent 2
client 2 rxed 8
client 0 rxed 4
main program ended

Review

  • Channels are a major feature of GO.
  • They allow goroutines to communicate with each other in a straightforward fashion.