In the last post, we looked at goroutines. Goroutines allow us to deploy multiple threads in a concurrent fashion. In this post, we will be looking at channels in Go. Channels in go let these goroutines communicate with one another.

Channeling and Piping

With goroutines, we have the ability to do multiple things at once. However, we do not have the ability to synchronize the threads effectively when we want them to communicate. This is where channels come in. Channels allow us to synchronize the execution of these concurrently running functions. They also give us a mechanism by which we can pass values from one thread to another safely.go-channel

Channels have several different characteristics: the type of data, the buffer size, and the direction of communication. You may specify all of these features by using the built in channel operator <-. Lets look at some basic examples.

As with many of the other complex data structures in go, we can use the make operator to create a channel. We write chan followed by the type of information we want to send. We can further change the definitions of the channels by adding the arrow operator <- before or after chan. This arrow operator tells us which direction the channel flows in; placing it before indicates that the channel will be read only and placing it after makes the channel write only. Finally, we can indicate the buffer size by passing a second argument to the make statement.

Channels are first class elements in Go. This means that they can be used almost anywhere; in struct fields, function arguments, function return statements, and even other channels. Lets look at some sample code of this.

Using Channels in Go

Now that we have a basic understanding of what channels are, let us actually see them in action.

In this example, we create a channel in the main function body. This channel takes in a Boolean value and has no specified buffer. We send the channel into our function which is a goroutine, this goroutine returns true and then back in the main function we wait for a value to be passed through the channel. Unlike our examples from last post, we don’t need to add a fmt.Scanln() function at the end because the program will only end after the channel reads a value from the goroutine. This happens because our channel has no specified buffer.go-channels

In Go, all of the operations on these unbuffered channels block or stop the execution until the sender and receiver are ready to communicate with one another. If a channel has a buffer in Go, all read operations succeed without blocking if the buffer is not empty and write operations will not block if the buffer is not full. Unbuffered channels are called synchronous channels while buffered channels are called asynchronous channels. Lets look at a buffered example.

In this example, we’ve given our channel a buffer of 3. Our code will run through all of the goroutine iterations before the time.Sleep execution call. If we remove the buffer, only one iteration of the goroutine will run before time.Sleep is called. The buffer is letting all of the write operations happen without waiting for the first read. Without the buffer, the channel will only make one write before it has to read the value in the final line of code.

Conclusion

In this post, we took a look at channels in Go. We looked at unbuffered channels and we briefly looked at buffered channels. In our next posts, we will continue to look at channels and the other concurrent features in Go.