[This post has been translated to Russian by Vlad Brown.]
Channels and goroutines form the core of Go’s CSP-based concurrency mechanism. Read on to pick up some tips and tricks about channels, specifically “buffered” channels, that are commonly used as queues in a producer-consumer context.
Buffered Channels = Queues
Buffered channels are first-in first-out (FIFO) queues of bounded capacity. The capacity is fixed at the time of creation of the queue – queues cannot be resized on the fly.
Each item in the queue can be up to 64KiB in size, and may or may not contain pointers. If you do have pointers, or if the item iself is a pointer, it is up to you to ensure that the pointed-to objects remain valid while it is in the queue and being consumed.
Producers (code that pushes items into the queue) can enqueue items with or without blocking:
Consumers typically pop items off the queue and process them. If the queue is empty and the consumer has nothing better to do, it can block until a producer pushes an item.
If the consumer should not wait, use this:
Closing Buffered Channels
Buffered channels are best closed by producers. The channel close event is signalled to the consumers. If you need to close the channel from outside of the producer or the consumer, you must use external synchronization to ensure that the producer does not attempt to write to a closed channel (this will cause a panic).
Reads and Writes to Closed Channels
Can you write to closed channels? Nope.
And reads from closed channels? Actually, before scrolling down, guess what this program outputs:
Here’s a playground link.
Surprised? If you are, remember you read it here first! :-)
Reads on closed channels behave differently:
- If there are items yet to be popped off, the popping off happens as usual.
- When the queue is empty and closed, the read will not block.
- Reads on empty and closed channels will return the “zero-value” of the channel item type.
That should let you figure out why the program printed out what it did. But how do you tell if the read was valid or not? The zero-value might be valid, after all. This is the trick:
This let’s you write consumers like this:
In fact, the “for..range” loop is an easier way of writing this exact same thing:
And finally, we can also combine the non-blocking and valid checks into one:
Go Consulting & Training
Need help getting a project off the ground using Golang? We have extensive experience in creating and running production grade Go-based software solutions. We can help you architect and design Go-based projects, or provide advice and mentoring for teams that work with Go. We also provide training for teams looking to get started with Go or to extend their Golang knowledge. Find out more here or contact us today to discuss your requirements!