In Go, a channel is a means of transferring data between goroutines with synchronization. There are two types:
Using unbuffered channels is convenient where synchronization is critical, and it is necessary to ensure that the sender and receiver meet at the same code location. Buffered channels are suitable for data transmission "with a buffer" — for organizing queues, workers, and asynchronous tasks.
func main() { ch := make(chan int) // unbuffered go func() { ch <- 42 // blocks until there is a receiver }() fmt.Println(<-ch) bufch := make(chan int, 2) // buffered with 2 elements bufch <- 1 // does not block bufch <- 2 // does not block // the next send will block if there is no reading }
Can a buffered channel serve as a replacement for a queue between dozens of producers and consumers without additional synchronization?
It is often answered — "yes". In reality, it’s only true if there are no guarantees about order and messages are not lost. With multiple readers/writers, situations of race conditions and data loss can occur, so synchronization control is still necessary (worker pool, mutexes, contexts for closing channels).
Story
In log processing, programmers used a buffered channel of length 1000 to transmit events between the parser and aggregator. When stopping the service, they forgot to close the channel, and some events were "lost" — workers finished before processing the entire buffer. It was fixed by explicitly closing channels and blocking until full unloading.
Story
When trying to replace a mutex queue with a buffered channel in a distributed service, they did not account for "hanging" senders: when the buffer was full, send blocking eventually slowed down the entire system, caused timeouts, and the interface started to "lag". After analysis, part of the synchronization was returned through condition variables.
Story
In a telemetry module, an unbuffered channel was used for error logging between the module and the consumer. Due to random processing delays, the writer was blocked, and within a minute a queue of hundreds of thousands of data accumulated in goroutines — resulting in "out of goroutines". It was fixed by replacing the unbuffered channel with a buffered one and an asynchronous worker to clear the queue.