This example uses Golang's unbuffered channels. Unbuffered channels contain one value at a time. Hence reason they are called a blocking channels and created like this: make(chan TYPE). To demonstrate the usage, we are going to greet cities one by one every 2 seconds. However, if there is 5 seconds delay in greeting, we will timeout the application. Please read the inline comments for detailed explanation.


Example 1


If you increase sleeping time from 2 to 5/+ seconds below, the application would timeout without printing anything at all. There isn't a default block in select statement so it will block until either a message is received through our channel then carry on blocking or encounter a timeout case automatically then exit. The main function is the receiver and the welcome function is the sender in this case.


package main

import (
"fmt"
"os"
"time"
)

func main() {
// List of cities.
cities := []string{"London", "Istanbul", "Berlin", "Madrid"}

// Create unbuffered channel.
ch := make(chan string)

// Pass channel and cities to welcome function and run it as goroutine.
go welcome(ch, cities)

// Indefinitely wait for the channel to return something.
for {
select {
// If something was received through the channel, print it.
case msg := <-ch:
fmt.Println(msg)
// If nothing was received through the channel for 5 seconds, timeout.
case <-time.After(5 * time.Second):
fmt.Println("timeout")
os.Exit(0)
}
}
}

// welcome accepts write-only channel and list of cities.
func welcome(ch chan<- string, cities []string) {
// Loop through the cities.
for _, city := range cities {
// Sleep 2 seconds before writing to the channel below.
time.Sleep(1 * time.Second)

// Write (send) greeting message to the channel.
ch <- fmt.Sprintf("Welcome to %s", city)
}
}

Output


$ go run -race cmd/client/main.go
Welcome to London
Welcome to Istanbul
Welcome to Berlin
Welcome to Madrid
timeout

Example 2


This is simpler version because we don't use timeout. Instead, we close the channel explicitly.


package main

import (
"fmt"
"time"
)

func main() {
// List of cities.
cities := []string{"London", "Istanbul", "Berlin", "Madrid"}

// Create unbuffered channel.
ch := make(chan string)

// Pass channel and cities to welcome function and run it as goroutine.
go welcome(ch, cities)

// Indefinitely wait for the channel to return something.
for msg := range ch {
fmt.Println(msg)
}

fmt.Println("done")
}

// welcome accepts write-only channel and list of cities.
func welcome(ch chan<- string, cities []string) {
// Loop through the cities.
for _, city := range cities {
// Sleep 2 seconds before writing to the channel below.
time.Sleep(1 * time.Second)

// Write (send) greeting message to the channel.
ch <- fmt.Sprintf("Welcome to %s", city)
}

// Close the channel so that range loop knows when to exit.
close(ch)
}

Welcome to London
Welcome to Istanbul
Welcome to Berlin
Welcome to Madrid
done

Example 3


In this case, we are passing cities one by one instead of all.


package main

import (
"fmt"
"time"
)

func main() {
// List of cities.
cities := []string{"London", "Istanbul", "Berlin", "Madrid"}

// Create unbuffered channel.
ch := make(chan string)

for _, city := range cities {
// Pass channel and city to welcome function and run it as goroutine.
go welcome(ch, city)

// Indefinitely wait for the channel to return something.
for msg := range ch {
fmt.Println(msg)

break
}
}

fmt.Println("done")
}

// welcome accepts write-only channel and city.
func welcome(ch chan<- string, city string) {
// Sleep 2 seconds before writing to the channel below.
time.Sleep(1 * time.Second)

// Write (send) greeting message to the channel.
ch <- fmt.Sprintf("Welcome to %s", city)
}

Welcome to London
Welcome to Istanbul
Welcome to Berlin
Welcome to Madrid
done