Go Channels
Go is my favorite language, without a doubt! Readable, simple code, active and friendly community, fast and modern language with GC, and a PERFECT vendoring system (absolutely love it). I realized that the channel mechanics in Go are not completely clear for some people, so let me explain!
Basically, everything is really simple. We have goroutines—it’s like coroutines, but go-routines, did you catch it? :) So we have independent threads using this. Channels are just a mechanism for communication between different goroutines in our program. It’s not limited to use with parallel goroutines, but, to be honest, it’s pointless to use channels outside of goroutines.
Since this is really simple, let’s just write a short piece of code and see how it works.
In this simple example, we are using an unbuffered channel. First, we create the channel, which will operate with the datatype “string”. In our goroutine (anonymous function in this example), using “<-”, we send the message to the channel. You can add time.Sleep() to make it more transparent and easy to understand. Then, in our main() function, we wait until channel “ch” receives any message, and as soon as it is received, we assign it to “msg”. Until it’s received, our main() function will wait for the message and will not pass through “msg := <-ch”. When the message is received, just print it. That’s all. We can send and receive messages to the channel from any part of our program, and it’s the core concept of it. You can call it a “pub/sub system” - it’s the same mechanics.
But more interesting is how buffered channels work. They can receive more than one message and store the messages until someone takes them. When the buffer of our channel is full, it will not receive new messages, and senders will have to wait until free space in the buffer is available.
First, we create the channel, which will receive messages with datatype “int” and will be able to store up to 3 messages. Then, look carefully, we create a loop in our goroutine, which will send more than 3 messages. After this goroutine sends the first 3 messages, it will be blocked and will wait until the channel has free space to receive other messages. Let’s go to our main() function… After launching the goroutine, it waits for 1 second. Then, we simulate that our program needs 0.5 seconds to process each received message. As soon as we receive the first one, it is taken out from the buffer of our channel, and the goroutine will be able to send the next message there. As soon as the last message is “processed” (with simulation), the “for” loop will detect the empty buffer of the channel and will end. In this way, we reach “fmt.Println(“Main: All messages received.”)” and the end of the program; our main() function will return the exit code to the OS.
Easy? Easy! Cool? Cool! :)
I hope this short explanation will help newcomers to this absolutely wonderful language and you will love it. Go has many tools for concurrency. Basically, concurrency is what this language was made for. Next time, I will tell you about other cool and useful tools. Now, as a bonus, I want to present an implementation of my concept idea about how to make WorkGroup-like coroutine management in C! Yes, I love it in Go so much, and I wish other languages had similar mechanics. If you don’t know how WorkGroup works in Go - just stay tuned, I will tell you about it in one of the next articles.