Golang makes writing concurrent code easy. The language itself comes with facilities to achieve concurrency easier than ever before. But, for someone starting with Golang the intuition behind using Golang’s concurrent constructs are tricky.

In the series of posts to come, I’ll talk about some of the tips and tricks behind writing concurrent piece of code to help all of you in understanding the intuition behind them with real world analogies. I’ve done my best to leave behind the bias of working on Golang for the past 10 months and give a fresh perspective to help understand the concurrent constructs of Golang in an easy way. Here starts the journey.

First let’s start with the concept of how to run a piece of code concurrently. To make the intuition easy, let’s first write a piece of code without leveraging/making use of concurrency.

Let’s make it concurrent and see the difference:

Placing a go statement before a function call starts a concurrent execution of that function, this concurrent thread of execution is called a goroutine.

Can I run many more such Goroutines by using the go construct???

Yes , you could spin up any number of goroutines by using the go construct for each invocation.

We’llllllll, that was easy, now I know how to execute concurrently, What are the other challenges ??

The other challenge is to be able to synchronize and pass messages between these concurrently running Go routines. Let’s first understand the problem better with a sample code. Here is a synchronization challenge:

Here is what’s happening in the above code: my_func() is called concurrently and it sleeps for a millisecond in every iteration and this gives the main function enough time to iterate 10 times and finish its printing.

The challenge is to achieve synchronization in a way that the print statement from main and the my_func are interleaved one after another.

Wow, that seems like a challenge, how to achieve it?

Go has a primitive which allows easy communication and synchronization between Goroutines, these are called channels.

How could I imagine Golang channels?

This is how !

Golang Channels can be visualized as a bridge between Goroutines; a bridge which can load a finite number of people. If the bridge has space for 10 people it’s not possible to accommodate more than 10 at any point in time. Once the bridge is full it could accommodate new people only after one or more of the passengers on the bridge are received on the other end. I’ll be using the same analogy for rest of the article when describing channels.

Let us look at some code snippets to see channels working:

Grrrrrr!! This is what you’ll get to see when you run the above code snippet!

Let’s dig deeper and understand how to deal with Golang channels.

The rule is simple: write into a channel blocks, when the number of elements inside the channel is equal to the size of the channel. It’s like trying to push more people into a full train and it’s obviously blocking!

blocks when the capacity is full

In the above code snippet the channel is of size zero, it doesn’t have buffer/space to accommodate more elements, thus the write into it blocks till any other go routines reads from the same channel. Since there is no other go routine trying to read from the channel it just blocks forever which leads to a deadlock!

Oh, then just a single write into the channel shouldn't block if the channel has enough space to accommodate one or more elements in its buffer???

Yes, absolutely right.

Then how do I create a space/buffer within the channel to be able to accommodate more?

This is how !

What if I push one more element into the channel ?

Wellllllllll….. Try let’s try it out!

You guessed it right. Deadlock again!

What if I make the size of the buffer to be 2,3,…..?

Sure, it’ll still continue to not to block till it’s full.

Now, since we’ve understood the blocking unblocking nature of channels, let’s get back to the primary purpose for which channels are used - that is to synchronize and safely share data between concurrently running goroutines. Let’s now see how to receive the data sent through the channel in a different goroutine.

The trick is to share the reference of the channel with the goroutines those you desire to synchronize and communicate.

Here are the steps:

Create a channel of desired capacity Call a function in a new go routine and share the reference of the channel with the goroutine/goroutines with which you wish to communicate across. You’ve done all the hard work now, start sending the data through the channels through one or many goroutines and receive it in the other.

This part is incomplete without a code snippet, the comments in the code snippet gives a comprehensive coverage of the inside story :D

To wrap up the first post let’s solve the synchronization challenge discussed above using the power, ease and privilege of Golang channels which we just discussed.

The synchronization challenge is solved and the output from the Goroutine and main function is interleaved one after the other.

That’s all from this post! Code samples and their ready to be run links are made available. Experiment around. It would be easy to follow along on executing the code snippets! I will soon some back with the second part of the episode….. Happy coding :)