# Gist of Go: Channels

## Metadata
- Author: [[Anton Zhiyanov]]
- Full Title: Gist of Go: Channels
- Category: #articles
- Summary: Go channels allow safe communication between goroutines. Writers can close a channel to signal completion, and readers can check if the channel is closed. Buffered channels can help manage concurrency by allowing goroutines to send values without blocking immediately.
## Highlights
- Go has a mechanism that solves this problem:
• The writer can *close* the channel.
• The reader can detect that the channel is closed.
The writer closes the channel using the `close()` function: ([View Highlight](https://read.readwise.io/read/01jv303n4y52vjddk8a9t0jxpz))
- A channel can only be closed once. Closing it again will cause a panic ([View Highlight](https://read.readwise.io/read/01jv306rwh3vagva8hznfnx8sn))
- You also can't write to a closed channel ([View Highlight](https://read.readwise.io/read/01jv3073yyw9s5xbyecdv9x6vw))
- Here are two important rules:
1. *Only the writer can close the channel, not the reader*. If the reader closes it, the writer will encounter a panic on the next write.
2. *A writer can only close the channel if they are the sole owner*. If there are multiple writers and one closes the channel, the others will face a panic on their next write or attempt to close the channel. ([View Highlight](https://read.readwise.io/read/01jv307p4k5x1rzjx85s5az97n))
- **Should I always close a channel?**
If you've ever worked with external resources (such as files or database connections), you know they should always be closed to prevent leaks. But a channel isn't an external resource. When a channel is no longer used, Go's garbage collector will free its resources, whether it's closed or not.
The only reason to close a channel is to signal to its readers that all data has been sent. If this isn't important to the readers, then you don't need to close it. ([View Highlight](https://read.readwise.io/read/01jv309121sddxw6a46hmtt4kp))
- Note that range over a channel returns a single value, not a pair, unlike range over a slice ([View Highlight](https://read.readwise.io/read/01jv30ba7jawvw4dvbk5z7c5d0))
- Everything works fine now, but if I come back to the code in a month and I'm not too careful, I could easily break it.
For example, if I close the channel from the reader function ([View Highlight](https://read.readwise.io/read/01jv30zya6qwq2dtqt6tkf775r))
- Or accidentally read from the channel in the writer function ([View Highlight](https://read.readwise.io/read/01jv3101f7v46ecqhk6p97m78c))
- These errors occur at runtime, so I won't notice them until I run the program. It would be better to catch them at compile time.
You can protect yourself from this kind of errors by setting the channel direction. Channels can be:
• `chan` (bidirectional): for reading and writing (default);
• `chan<-` (send-only): for writing only;
• `<-chan` (receive-only): for reading only ([View Highlight](https://read.readwise.io/read/01jv310kenyhtvjkmjt43c1ac6))
- You also can't close a receive-only channel. Uncomment line ➌, and you'll get a compile error ([View Highlight](https://read.readwise.io/read/01jv311ganswbm3m8end7rjsbj))
- So, channels are usually initialized for both reading and writing, and specified as directional in function parameters ([View Highlight](https://read.readwise.io/read/01jv312jmf1sy1kck8pm0a9r71))
- The most common problem in concurrent programs is a deadlock. A deadlock occurs when one goroutine waits for another, and vice versa. Go detects such situations and terminates the program with an error.
fatal error: all goroutines are asleep - deadlock! ([View Highlight](https://read.readwise.io/read/01jv38sd436hctskqhxnjd0kdn))
- Goroutines are lightweight. You can easily start 10, 100, or even 1,000 at once. But what if you have a million phrases? Real concurrency is still limited by the number of CPU cores. So, it's pointless to waste memory on hundreds of thousands of goroutines when only eight (or however many CPUs you have) can run concurrently. ([View Highlight](https://read.readwise.io/read/01jv3aqb13nax2e2hxpzmnxx1h))
- Let's say we want only N `say` goroutines to exist at the same time. A buffered channel can help achieve this. Here's the idea:
• Create a channel with a buffer size of N and fill it with "tokens" (arbitrary values).
• Before starting, a goroutine takes a token from the channel.
• Once finished, the goroutine returns the token to the channel. ([View Highlight](https://read.readwise.io/read/01jv3ayhkwdjd2t5nh39nf7g68))
- In practice, you'll often find an "inverse" approach to implementing semaphores:
• Create an empty channel with a buffer size of N.
• Before starting, a goroutine puts a token into the channel.
• Once finished, the goroutine takes a token from the channel. ([View Highlight](https://read.readwise.io/read/01jv3b57vc0r60jm0p7ghn2mnx))
- As long as there are values in the buffer, the channel returns those values and a `true` status. Once all values are read, it returns a zero value and a `false` status, just like a regular channel.
This allows the sender to close the channel at any time without worrying about leftover values. The receiver will read them anyway ([View Highlight](https://read.readwise.io/read/01jv3bqy1yy4z1a4x32mn89rqv))
- Like any type in Go, channels have a zero value, which is `nil` ([View Highlight](https://read.readwise.io/read/01jv3brp0csyfw29abwkkra2e5))
- A nil channel is an ugly beast:
• Writing to a nil channel blocks the goroutine forever.
• Reading from a nil channel blocks the goroutine forever.
• Closing a nil channel causes a panic. ([View Highlight](https://read.readwise.io/read/01jv3bs1jvskxm8mgq130m1c6p))
- Nil channels can be useful in certain cases. We'll look at one of them in the next chapter. In general, try to avoid nil channels unless you absolutely need them. ([View Highlight](https://read.readwise.io/read/01jv3bsak9225tnbvggcb9aqfj))