Let’s GO! Part 12: Reference Types 4: Channels

Ali Azizjahan
5 min readFeb 11, 2022
Concurrency is when more than 1 gopher are digging holes at the same time.

Overview

Channels are here to solve the concurrency problem in GO! They’re basically some bunch of pointers as well that hide the hardness of implementing FIFO queues under the hood to read from and write to.

Concurrency, You Say?

Yes! Concurrency is when you want to run more than one block of code at the same time. Concurrent jobs in GO are called goroutines and GO, under the hood, is very efficient when it comes to concurrency as it sends each job (block of code) to a different CPU core to be executed as a completely separated thread. This is unlike some other programming languages like Javascript that your entire program gets run on only one single thread thus they can’t take advantage of multicore systems, making them not very efficient when it comes to concurrent jobs.

How To Run Something In A Concurrent Manner?

Say go before it. Yup! That simple! When you wanted to run a function in the usual synchronized way, you’d simply say it’s name like myFuncName(). Now if you want to run it in an asynchronized manner, just say go myFuncName() and it’ll be sent to it’s own thread to be executed, thus it won’t block the next lines of codes that come after it to get executed like it did in the synchronized way. Now if you want to know that how concurrency helps to solve some major problems in programming, you’re a Google search away from your answer.

So What Are Channels?

Channels, in appearance, are basic variables that work like a FIFO queue. However, their syntax is a little bit different than simple variables. At the first glance they might look a little hard to grasp but believe me they’re not that hard. As soon as you know how do they work, you’ll get addicted to using them left and right! The idea of the Channels is to solve the mutual exclusion issues when working in an asynchronous way by reversing the dependencies. Read this sentence carefully a few times to understand how Channels do such job:

Instead of trying to establish the communication across your application by sharing the memory, Channels try to share the memory by establishing the communication across your application.

It might sound hard a little bit but it’s really not. As you get more familiar with them you’ll see this yourself in action.

So, How Do They Work?

We have receive-only Channels, which you can only receive (read) values from them, and we have send-only Channels, which you can only send (write) values to them, and we have receive/send Channels which you can perform both of the operations on them. Based on your use case, you can decide which one to use. For example if your function only wants to read the value from the Channel and never intends to write a value to it, you may want to pass your receive/send Channel as a receive-only Channel to your function to prevent logical errors. But what is really happening with Channels is that when you try to read from a Channel, GO will wait for a value to be written to it if there’s no values inside it already. And if there are multiple values inside a Channel, GO will pop the first value that is sent to it and gives it back to you when you try to read from it once. (Hence the FIFO structure) It means that the second time you try to read, you’ll get the second value. So any written values to a Channel can be read only once, and after that, the value is gone. Try to think of it as a tube; A block of code pushes a value inside the tube. The other end of the tube is where you can receive the value from it. But once you get the value out of the tube (read it) it’s not inside the tube for anyone else to receive anymore. Now if you try to read from a Channel that you never send to it anywhere inside your code flow, GO’s compiler will complain that it is a deadlock, because it waits for an infinite time for a value to be sent inside the tube but it never happens. Thus the next lines of code will never be executed and GO is very intelligent finding where the things are going wrong.

Syntax! Shall We?

Since the syntax of the Channels is a little bit different than simple variables, it’s a good thing to know it before we see them in action.

The birth of a Channel:

# The "CHANNEL_TYPE" can be any type that we already know
# But it should be prefixed with "chan"
myChannelName := make(chan CHANNEL_TYPE)

Writing (sending) to a Channel:

# the "VALUE" should be of the type that the Channel is made of
# It's basically saying "send the value inside the tube (queue-Channel)"
myChannelName <- VALUE

Read from a Channel:

# Get the latest value of a Channel in a VariablemyVariableName := <-myChannelName# OR use the value directly. Either way it's now popped out of the queuefmt.Println(<-myChannelName)

Note the <- that does the read and write operations on Channels based on whether it comes before or after the Channel’s name respectively.

Typing a Channel as an argument:

# Receive/send Channelfunc myFunctionName(myChannelName chan CHANNEL_TYP) {# Now you can perform both read and write ops on the Channel
...
}
# Receive-only Channelfunc myFunctionName(myChannelName <-chan CHANNEL_TYP) {# Now you can only receive (read) values from the Channel
...
}
# Send-only Channelfunc myFunctionName(myChannelName chan<- CHANNEL_TYP) {# Now you can only send (write) values to the Channel
...
}

Aight we good. Let’s see the code.

Code.

Channels in GO

The result should be:

144

Some important notes about the above code:

  • The Pow function that comes from the built-in math package simply powers it’s first argument by it’s second argument. Both arguments should be of type float64 and so the result.
  • As you can see in the code, if you run two goroutines at the same time and they’re dependent, you’ll run into a race condition! Be very careful with this. Not everything needs to be asynchronous. Only the jobs that are time consuming and are not dependent on some other jobs that are running on some other simultaneous goroutines. But if you run everything on a synchronized manner you won’t have this race condition problem. However in that case, if the square function is calculating something that it’s a little time consuming (say that it’s making an HTTP request inside it), you’re entire program is blocked until it does it’s job. Try messing around in your IDE/Editor and you’ll eventually know how to thing like GO.

Are we good now? Hell yeah! Next we’re gonna talk about the Function Receivers. Buckle up!

Concurrently dancing gophers. (Please pretend that these are gophers because I couldn’t find a gif of a group of gophers dancing together)

Next:

Coming soon…

Previous:

List Index:

--

--

Ali Azizjahan

Software Engineer and Computers Researcher. Teaching you the things that I wish I knew myself sooner, or I could be taught way easier. https://linktr.ee/kyxey