I recently watched Rob Pike's talk on Concurrency Patterns in Go, and was really impressed by how easy it was to make solid concurrent code without relying on threads, mutexes/locks or anything more complicated than a channel -- which I'm beginning to learn is one of the coolest things about Go, and has some incredibly elegant simplicity.

Rob Pike's talk finished with a slightly complex example of a concurrent web-search with a timeout, which I found on Github and decided to adapt slightly for myself, with some more comments and helper functions to make it super-easy to read.

I also learnt in some more detail how the select loop works, which Rob didn't go into in much detail. It's a really cool high-level abstraction! My understanding is that it's quite like a switch/case statement, which waits for something interesting to happen and delegates to the appropriate block of code. For example:

        select {
            case result := <-c:
                results = append(results, result)
            case <-timeout:
                fmt.Println("timed out");

This code waits for a value to be pushed onto either of the channels c or timeout:

  • If it's c that's received the value, that value gets pulled out into a value called result, and appended to the results array.

  • If timeout has got a value, we print an error message and return straight away.

I also used a neat shortcut for "extract a value from one channel and put it onto another, like so:

result <- <- c

My understanding is that <- c gets evaluated to a value v first, then result <- v has the expected behaviour of placing v onto the channel.

What I'm enjoying about Go right now is that with regard to concurrency, the structure of a program seems incredibly intuitive to anyone familiar with the idea of queues. I'm not worrying about whether I have a certain number of threads running, or whether my mutexes are going to cause a deadlock, or whether the number of locks I've got are rendering my application essentially single-threaded -- I'm just specifying a bunch of "things to do", and Go handles multiplexing them over system threads.

That's not to say Go doesn't have mutexes and locks -- but a lot of the time, it's very likely that channels and goroutines are a more elegant (and probably more efficient) way of doing things.

Have a look at my slightly-adapted search engine example here: