Go Concurrency 101

Goroutines

Goroutines enable concurrent behaviour in Go programs. To make a function concurrent – non blocking – simply place the go keyword before the function invocation.

func helloWorld() {
  fmt.Println("Hello, World")
}

// invoke the function concurrently
go helloWorld()

Channels

Goroutines use channels to communicate. The type of data a channel can transfer is determined at channel creation time. The snippet below creates an integer channel telephoneLine and passes the channel to the neighbour goroutine.

telephoneLine := make(chan int)
go neighbour(telephoneLine)

Then the neighbour can communicate to us through the telephoneLine channel. In the code below, the neighbour sends us a message 1 and closes the telephoneLine. It is idiomatic for the goroutine that writes to a channel to close it when done.

func neighbour(telephoneLine chan<- int) {
  telephoneLine <- 1
  close(telephoneLine)
}

We receive the message by reading from telephoneLine.

msg := <- telephoneLine

// prints 1
fmt.Println(msg)

Select

Goroutines deadlock when not accessed in the same order. Assuming we have two telephone lines line1 and line2 and in a goroutine we first write to line1 then read from line2.

line1 := make(chan int)
line2 := make(chan int)

go func() {
  line1 <- 1
  msg <- line2
}

Then if we write to line2 and read from line1 in another goroutine the program deadlocks and panics.

line2 <- 2
msg := <- line1

However, with the select keyword this won’t happen because it executes whatever case that can proceed.

select {
  case line2 <- 2:
  case msg := <- line1:
}