I think the specs are pretty clear about this. Spec: channel types:
A channel provides a mechanism for simultaneously executing communication functions by sending and receiving values of a specified type of element.
If you have several routines that run at the same time, channels provide the easiest way to allow routines to communicate with each other.
One way to communicate may be to use a “shared” variable that is visible to both routines, but this will require proper locking / synchronized access.
Instead, Go prefers channels. Quote from Effective Go: share while communicating :
Do not communicate, sharing memory; instead, share your memory while communicating.
Thus, instead of putting messages in a common slice, for example, you can create a channel (visible for both routines), and without any external synchronization / blocking, one routine can send messages (values) through the channel, and the other routine can get them.
Only one program has access to a value at any given time. Data racing cannot be provided.
Thus, in fact, any number of subprograms can send values on the same channel, and any number of subprograms can receive values from it, without any further synchronization. See the related question for more details: If I use channels correctly, do I need to use mutexes?
Channel example
Let's look at an example where we run 2 additional programs for simultaneous calculations. We pass the number to the first, which adds 1 to it and delivers the result on channel 2. The second program will receive the number, multiply it by 10 and deliver it to the results channel:
func AddOne(ch chan<- int, i int) { i++ ch <- i } func MulBy10(ch <-chan int, resch chan<- int) { i := <-ch i *= 10 resch <- i }
Here's how to call it / use it:
func main() { ch := make(chan int) resch := make(chan int) go AddOne(ch, 9) go MulBy10(ch, resch) result := <-resch fmt.Println("Result:", result) }
Communication through the channels also takes care of the goroutines awaiting each other. In this example, this means that MulBy10() will wait for AddOne() to deliver the increased number, and main() will wait for MulBy10() before printing the result. The conclusion is expected (try on the Go Playground ):
Result: 100
Language support
There are several language constructs designed for convenient use of channels, for example:
for ... range on the channel iterates over the values received from the channel until the channel is closed.- The
select statement can be used to list several channel operations, such as sending on the channel and receiving from the channel, and one that can continue without blocking will be selected (randomly, if there are several operations that can continue and block, if none are ready ) - There is a special form of the receive statement that allows you to check whether the channel has been closed (in addition to receiving the value):
v, ok := <-ch - The built-in
len() function reports the number of elements in the queue (unread); The construction cap() function returns the capacity of the channel buffer.
Other use
For a more practical example, see how you can use channels to implement a work pool . Similarly, the distribution of values from producer to consumer (s) is used .
Another practical example is the implementation of a memory pool using buffered channels .
And another practical example is an elegant broker implementation .
The channel is often used to time out some blocking operation, using the channel returned by time.After() , which “fires” after the specified delay / duration (“fires” means that a value will be sent to it). Check out this example for a demo (try on the Go Playground ):
ch := make(chan int) select { case i := <-ch: fmt.Println("Received:", i) case <-time.After(time.Second): fmt.Println("Timeout, no value received") }
It can be used to wait for the maximum amount of time for a certain value, but if other procedures cannot provide a value by this time, we can instead decide to do something else.
Also, a special form of communication can simply signal the completion of an operation (without actually sending any “useful” data). Such a case can be implemented by a channel with any type of element, for example, chan int and sending any value to it, for example, 0 . But since the value sent does not contain information, you can declare it as chan struct{} . Or, even better, if you only need a one-time signaling, you can simply close the channel, which can be intercepted on the other side using for ... range , or receive from it (since reception from the closed channel occurs immediately, giving a zero value type of item). Also be aware that although a channel can be used for this kind of signaling, there is a better alternative for this: sync.WaitGroup .
Further reading
To avoid unexpected behavior, you should know about the axioms of the channel: How does an uninitialized channel behave?
Go blog: share memory while chatting
Go Blog: Go Concurrency Patterns: Pipelines and Undo
Go Blog: Advanced Go Concurrency Patterns
Ardan labs: the nature of channels in motion