Concurrent programming in Go: channels

Go is designed to be a concurrent programming language as stated earlier. One nice feature built into the language is the support for pipelines. The official blog [1] defines the pipeline as:

a series of stages connected by channels, where each stage is a group of goroutines running the same function

So what is channels in Go? The Go programming language specification [2] defines it as

A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type.

Let's look at an example that's taken from Go by Example [3]. You should see ping as the output.

// go run channels.go
package main
import "fmt"

func main(){
    messages := make(chan string)

    go func() { messages <- "ping"}()

    msg := <-messages
    fmt.Println(msg)
}

In the example we created a channel called messages and then send a "ping" string through the channel using a goroutine. Then we receive the msg from the channel and print the result out.

Channel is designed to be blocking hence it is extremely useful in concurrent synchronization without explicit lock [4].

However, channels can be made non-blocking. Here is an example showing how to use non-blocking channels [5].

// go run channels-non-blocking.go
package main

import "fmt"

func main() {
    messages := make(chan string)
    signals := make(chan bool)

    // Here's a non-blocking receive. If a value is
    // available on `messages` then `select` will take
    // the `<-messages` `case` with that value. If not
    // it will immediately take the `default` case.
    select {
    case msg := <-messages:
        fmt.Println("received message", msg)
    default:
        fmt.Println("no message received")
    }

    // A non-blocking send works similarly.
    msg := "hi"
    select {
    case messages <- msg:
        fmt.Println("sent message", msg)
    default:
        fmt.Println("no message sent")
    }

    // We can use multiple `case`s above the `default`
    // clause to implement a multi-way non-blocking
    // select. Here we attempt non-blocking receives
    // on both `messages` and `signals`.
    select {
    case msg := <-messages:
        fmt.Println("received message", msg)
    case sig := <-signals:
        fmt.Println("received signal", sig)
    default:
        fmt.Println("no activity")
    }
}

You should see the following result:

no message received
no message sent
no activity

[1] https://blog.golang.org/pipelines

[2] https://golang.org/ref/spec#Channel_types

[3] https://gobyexample.com/channels

[4] https://tour.golang.org/concurrency/2

[5] https://gobyexample.com/non-blocking-channel-operations