Classical Concurrency Patterns for Gophers


Concurrency is one of the most discussed topics is programming. A lot of concurrency patterns have been introduced over time in different programming languages. Golang is considered as a language with very rich support for concurrency. This article takes a deep dive into how these classical concurrency patterns can be adapted into Golang.

Golang’s rich support for concurrency is mainly based on two primitives

  • Goroutines
  • Channels

Two important principles about Go concurrency primitives,

  • Start goroutines when you have concurrent work
  • Share by communicating (not using shared variables, but with channels)

Let’s dive into the concurrency patterns.

In languages like JavaScript asynchronicity has been heavily used to handle concurrency. But concurrency is not asynchronicity as well. A code which is programmed to wait until an asynchronous operation finishes and move onto the next operation is not concurrent. Here are some common asynchronous patterns.

Futures are a common concurrent pattern where it returns a proxy object instead of returning the result. The proxy object will resolve into the response later. It's up to caller method to decide, whether to wait for the response or proceed with other tasks and use the response later. In Go, this pattern can be implemented using a single element buffered channel.

Producer and consumer pattern is a concurrency pattern which helps to increase the scalability of the code. Unlike the previous scenario, we will use an unbuffered channel and the producer can expect multiple responses depending on the situation. Here is a simple implementation.

Usually, these asynchronous patterns are used when we need to avoid method calls being blocked (eg: UI and network threads). We don’t get that advantage in Golang Since the Go runtime manages goroutines. But since goroutines can return from any of the stack frames, we get the advantage of reclaiming the stack frames efficiently. Meanwhile, the GC can claim the stack allocations which became unreachable since the particular goroutine returned. Since the asynchronous functions return immediately, the caller can proceed to make other calls.

But these asynchronous patterns comes with a cost as well. The caller side code becomes less clear to understand in most of the other languages. But in Golang, converting synchronous to asynchronous and vice versa is easy. So we don’t need to worry about the cons of asynchronous when using Golang in the implementation.

Condition variables are another classic concurrency pattern. A more popular term for this pattern is monitors. This pattern has been in use for a long time now. We can easily implement this pattern using the Mutex in sync package in Golang. You can find the implementation in the below playground by Bryan C. Mills,

But this can be easily achieved using channels. It’s more elegant and clean compared to the mutex implementation. This implementation can be found in the following playground link by Bryan C. Mills.

There are multiple issues associated with the conditional variable same as other patterns. Let’s look into these issues with the following implementation,

type Queue struct {
items []Item
closed bool
mu sync.Mutex
itemAdded sync.Cond
func (q *Queue) Put(item Item) {
q.items = append(q.items, item)
func (q *Queue) GetMany(n int) []Item{
for len(q.items < n){
items = q.items[:n:n]
q.items = q.items[n:]
return items
  • If we broadcast (signalling to everyone who is waiting), it can result in waking up an unnecessary amount of waiters. although only one of them can actually complete.
  • If a GetMany call with n=300 and recurrentGetMany call with n = 3 is waiting, the call with n=300 can starve since every time Broadcast() is called, every worker gets woken up.
  • If a context gets cancelled while some goroutines are waiting, the waiting goroutines won’t be notified and the waiting will continue even after the context cancellation.

But using Golang channels will solve all of the above issues

c := make(chan Item)
// Putting something int the queue
c <- item
// Receiving something from the queue
receivedItem := <- c

Golang is a programming language rich in concurrency features. It allows you to implement most of the common concurrency patterns easily and elegantly. This article was heavily inspired by a Gophercon talk Bryan C. Mills. You can find the talk in the following video.

The Startup

Get smarter at building your thing. Join The Startup’s +792K followers.

By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Sudaraka Jayathilaka

Written by

Gopher 💻 Cloud ☁️ Distributed Computing 🖥

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +792K followers.

Sudaraka Jayathilaka

Written by

Gopher 💻 Cloud ☁️ Distributed Computing 🖥

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +792K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store