go go/book/learninggo

Introduction

What are channels ?

channel is a communications pipe between goroutines. Things go in one end and come out another in the same order until the channel is closed.

Creating Channels

func main() {
	var ch chan string
 
	fmt.Println(c)
}
go run main.go
<nil>

Warning

As we can see, the zero value of a channel is nil and if we try to send data over the channel our program will panic.


func main() {
	ch := make(chan string)
 
	fmt.Println(c)
}

output:

$ go run main.go
0x1400010e060

channel was initialized.

Sending and Receiving data

package main
 
import "fmt"
 
func speak(arg string, ch chan string) {
	ch <- arg // Send
}
 
func main() {
	ch := make(chan string)
 
	go speak("Hello World", ch)
 
	data := <- ch // Receive
	fmt.Println(data)
}

output:

$ go run main.go
Hello World

Notice how we can send data using the channel <- data and receive data using the data := <- channel syntax.

Buffered Channels

Buffered channels that accept a limited number of values without a corresponding receiver for those values.

func speak(arg string, ch chan string) {
	ch <- arg // Send
}
 
func main() {
	ch := make(chan string, 2) //2 <-- Buffer length
 
	go speak("Hello World", ch)
	go speak("Hi again", ch)
 
	data1 := <- ch
	fmt.Println(data1)
 
	data2 := <- ch
	fmt.Println(data2)
}

Directional Channels

When using channels as function parameters, we can specify if a channel is meant to only send or receive values. This increases the type-safety of our program as by default a channel can both send and receive values.

func speak(arg string, ch chan <- string) {
	ch <- arg // Send Only
}
// Function that receives a value from a channel
func listen(ch <- chan string) {
	// Receive a value from the channel and print it
	message := <- ch // Receive Only
	fmt.Println("Received:", message)
}

Closing Channels

func main() {
	ch := make(chan string, 2)
 
	go speak("Hello World", ch)
	go speak("Hi again", ch)
 
	data1 := <- ch
	fmt.Println(data1)
 
	data2 := <- ch
	fmt.Println(data2)
 
	close(ch) // close ch resource
}
func main() {
	ch := make(chan string, 2)
 
	go speak("Hello World", ch)
	go speak("Hi again", ch)
 
	data1 := <- ch
	fmt.Println(data1)
 
	data2, ok := <- ch
	fmt.Println(data2, ok)
 
	close(ch)
}

if ok is false then there are no more values to receive and the channel is closed.

Important

closing a channel only means “no more values will be sent to it.”
Reading from a closed channel is still safe.

Channel Properties

  • A send to a nil channel blocks forever.
var c chan string
c <- "Hello, World!" // Panic: all goroutines are asleep - deadlock!
  • A receive from a nil channel blocks forever.
var c chan string
fmt.Println(<- c) // Panic: all goroutines are asleep - deadlock!
  • A send to a closed channel causes a panic.
var c = make(chan string, 1)
c <- "Hello, World!"
close(c)
c <- "Hello, Panic!" // Panic: send on closed channel
  • A receive from a closed channel returns the zero value immediately.
var c = make(chan int, 2)
c <- 5
c <- 4
close(c)
for i := 0; i < 4; i++ {
    fmt.Printf("%d ", <-c) // Output: 5 4 0 0
}
  • Range over channels.

We can also use for and range to iterate over values received from a channel.

package main
 
import "fmt"
 
func main() {
	ch := make(chan string, 2)
 
	ch <- "Hello"
	ch <- "World"
 
	close(ch)
 
	for data := range ch {
		fmt.Println(data)
	}
}

Select Statement

The select statement blocks the code and waits for multiple channel operations simultaneously.

A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.

package main
 
import (
	"fmt"
	"time"
)
 
func main() {
	one := make(chan string)
	two := make(chan string)
 
	go func() {
		time.Sleep(time.Second * 2)
		one <- "One"
	}()
 
	go func() {
		time.Sleep(time.Second * 1)
		two <- "Two"
	}()
 
	select {
	case result := <-one:
		fmt.Println("Received:", result)
	case result := <-two:
		fmt.Println("Received:", result)
	}
 
	close(one)
	close(two)
}

Go - Waitgroup and Mutex