Channels.
Coroutines often need to communicate. One produces data, another consumes it. How do they pass data safely? Channels provide a way for coroutines to send and receive values. Today we learn how channels work.
A channel is a queue for coroutine communication.
Think of a channel like a pipe. One end sends values in. The other end receives values out. The channel sits between sender and receiver.
Channels are similar to BlockingQueue from Java, but designed for coroutines. Instead of blocking threads, they suspend coroutines.
One coroutine sends data to the channel. Another coroutine receives data from the channel. They do not need to know about each other. The channel is the intermediary.
This decouples producers from consumers. They communicate through the channel, not directly.
send puts a value into the channel.
channel dot send value. This suspends if the channel is full. The coroutine waits until there is room.
receive takes a value from the channel.
val value equals channel dot receive. This suspends if the channel is empty. The coroutine waits until a value arrives.
Both operations are suspend functions. They pause the coroutine, not the thread.
send and receive can happen in different coroutines. One sends, another receives. The channel coordinates them.
Create a channel with Channel constructor.
val channel equals Channel of Int round brackets. This creates a channel that passes integers.
Channels are generic. Channel of String passes strings. Channel of User passes users.
The constructor takes an optional capacity parameter. This determines how many values the channel can hold.
By default, channels have no buffer. Send suspends immediately until someone receives.
Rendezvous channel has zero capacity. Channel of T round brackets. Send suspends until receive happens. Receive suspends until send happens. They meet at the channel.
Buffered channel has fixed capacity. Channel of T capacity equals 10. Send only suspends when buffer is full. Receive only suspends when buffer is empty.
Unlimited channel has infinite capacity. Channel of T UNLIMITED. Send never suspends. Values queue up. Be careful with memory.
Conflated channel keeps only the latest value. Channel of T CONFLATED. Send never suspends. Old values are dropped if not received.
Choose the type based on your needs. Rendezvous for tight synchronization. Buffered for some decoupling. Unlimited or conflated for special cases.
A channel can be closed.
channel dot close round brackets. After closing, no more values can be sent.
Receivers can check if the channel is closed. receive throws ClosedReceiveChannelException on a closed empty channel.
Better: use receiveCatching or check isClosedForReceive.
Closing signals completion. The producer says: I am done. Receivers finish processing remaining values and stop.
Always close channels when done. This prevents receivers from waiting forever.
You can iterate over a channel with a for loop.
for value in channel brace block close brace. The loop receives values until the channel closes.
This is the cleanest way to consume a channel. No manual receive calls. No checking for closed.
The loop suspends when the channel is empty. It resumes when a value arrives. It exits when the channel closes.
Under the hood, the for loop uses the channel's iterator. Each iteration calls receive.
Close the channel to end the loop. Without closing, the loop waits forever.
A common pattern: one coroutine produces, another consumes.
Launch a coroutine that sends values, then closes the channel.
The producer owns the channel. It creates values, sends them, and signals completion by closing.
The consumer iterates over the channel. It processes each value until the channel closes.
This pattern is clean and safe. The producer controls the lifecycle. The consumer just processes.
Multiple consumers can receive from one channel. Each value goes to one consumer. This enables work distribution.
Consider processing a stream of events.
A producer coroutine fetches events from a source. It sends each event to a channel.
A consumer coroutine receives events. It processes each one.
The channel buffers events if the consumer is slow. The producer can stay ahead.
When the source is exhausted, the producer closes the channel. The consumer finishes remaining events and completes.
This decouples fetching from processing. Each can run at its own pace.
Let us summarize.
Channels are concurrent queues for coroutine communication.
send puts values in, receive takes values out. Both suspend when needed.
Channel types: rendezvous for tight sync, buffered for flexibility, unlimited or conflated for special needs.
Close channels to signal completion.
Iterate with for loops for clean consumption.
Channels decouple producers from consumers, enabling flexible concurrent patterns.