Go Channel(for IPC)
-
Go channels are used for synchronizing IPC between goroutines(Same as
Rust Tasks = Green Threads). Go routines are managed by Go runtime.
we can send/recv data via channel with operator, <- between goroutines. The data flows in the direction of the arrow.
channels are datastructures allocated on heap that reference both sender and receiver goroutines
close(): Only sender can close a channel to indicate that no more values will be sent.
|
|
select(wait on multiple channels)
-
Channels need not to be closed because channels are not files; you don't
need to close them
Code
-
1. Write/Read data on channels
package main
import (
"fmt"
)
func main() {
// 1. Declare 3 unbuffered channels ch1,ch2,done
ch1 := make(chan int)
ch2 := make(chan int)
done := make(chan int)
// 2. go routine. Send data on channels
go func() {
ch1 <- 1
ch2 <- 2
close(done)
}()
// 3. Keep waiting on channels
for {
select {
case v1, ok := <-ch1: // Recieve data on ch1
if ok {
fmt.Println("ch1:", v1)
}
case v2, ok := <-ch2: // Recieve data on ch2
if ok {
fmt.Println("ch2:", v2)
}
case <-done: // Receive data on done
fmt.Println("Bye!")
return
default:
// Print this, when no channel is ready
fmt.Println("No case ready")
}
}
}
$ go run main.go
ch1: 1
ch2: 2
Bye!
Types of Channels
| Buffered / Bounded | Unbuffered / Unbounded | |
|---|---|---|
| What |
When buffer length is provided a buffered channel is created
|
Channel created without any size
|
| No reciever | No issue | Memory Leak in goroutine |
1.
-
. Send to a buffered channel block only when the buffer is full.
Receives block when the buffer is empty.
Bidirectional / Directional
-
In Go, all channels start as bidirectional, but you can use directional
types to enforce API safety and prevent bugs.
| 1. Bidirectional | 2. Directional (Recieve only) | 3. Directional (Send only) | |
|---|---|---|---|
| What | Can both send and receive data | Data can only read data from it | Data can only put data into it |
| Format | (chan T) | <-chan T | chan<- T |
-
Why use directional Channels?
1. Safety: If we have a function that only processes results, we should use directional(RO) channel <-chan T. Using bidirectional may accidently send data on the channel
Internal Implementation of Channels
-
When we call make(chan int, 10), the runtime allocates a
struct hchan on heap
func main() {
ch1 := make(chan int)
}
// Channel struct
// (heap, hchan with buffer + sendq/recvq queues)
type hchan struct {
qcount uint // bytes queued
dataqsiz uint // buffer size
buf unsafe.Pointer // ring buffer (heap)
elemsize uint16 // elem size
closed uint32
elemtype *_type
sendx uint // send index
recvx uint // recv index
recvq waitq // blocked RECEIVERS
sendq waitq // blocked SENDERS ← your goroutine lives here
}
Why Channels are Fast
-
1. Direct Copying: If a sender is waiting and a receiver arrives, the Go
runtime copies the data directly from the sender's stack to the
receiver's stack, skipping the channel buffer entirely
2. No busy-waiting: Instead of "busy-waiting" (spinning), a blocked goroutine is "parked" by the Go scheduler.