Golang Tutorial
Fundamentals
Control Statements
Functions & Methods
Structure
Arrays & Slices
String
Pointers
Interfaces
Concurrency
Channels are a powerful feature in Go, central to Go's concurrency model and goroutine communication. They allow goroutines to communicate with each other and synchronize their execution.
Channels are typed conduits through which you can send and receive values using the channel operator (<-
). The data flows in the direction of the arrow.
To create a new channel, you use the built-in make
function:
ch := make(chan int)
This creates a new channel of type int
.
Here's how you send and receive data from channels:
ch <- 42 // Send a value to the channel value := <-ch // Receive a value from the channel
Let's see a simple example of how goroutines can communicate using channels:
package main import ( "fmt" "time" ) func main() { ch := make(chan string) go func() { time.Sleep(2 * time.Second) ch <- "Message from goroutine" }() message := <-ch fmt.Println(message) }
In this example, the main goroutine waits for the message from the child goroutine.
Channels are, by default, unbuffered. This means that they will block on send unless there's another goroutine ready to receive the value.
However, you can create buffered channels:
ch := make(chan int, 2)
With a buffered channel, sends are non-blocking as long as the buffer isn't full, and receives are non-blocking as long as the buffer isn't empty.
You can also use the range
keyword to iterate over values sent to a channel:
package main import ( "fmt" ) func main() { ch := make(chan int, 2) go func() { for i := 0; i < 3; i++ { ch <- i } close(ch) }() for value := range ch { fmt.Println(value) } }
In the above example, after sending all values to the channel, we close it using close(ch)
. The main goroutine uses range
to read values until the channel is closed.
The select
statement allows a goroutine to wait on multiple communication operations. It's like a switch
statement but for channels:
package main import ( "fmt" "time" ) func main() { ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(1 * time.Second) ch1 <- "Message from ch1" }() go func() { time.Sleep(2 * time.Second) ch2 <- "Message from ch2" }() for i := 0; i < 2; i++ { select { case msg1 := <-ch1: fmt.Println(msg1) case msg2 := <-ch2: fmt.Println(msg2) } } }
You can close a channel using the close
function. Receivers can also test whether a channel has been closed:
v, ok := <-ch if !ok { fmt.Println("Channel is closed") }
When a channel is closed, you can still receive values until it's empty, but you can't send values to it.
Channels are fundamental to Go's approach to concurrency. They provide a safe way to communicate between goroutines and synchronize their execution. Always remember the Go mantra: "Do not communicate by sharing memory; instead, share memory by communicating."
Channel Basics in Golang:
package main import "fmt" func main() { // Create a channel ch := make(chan int) // Send data to the channel go func() { ch <- 42 }() // Receive data from the channel value := <-ch fmt.Println(value) // Output: 42 }
How to Create Channels in Golang:
make
function to create a channel.package main import "fmt" func main() { // Create an unbuffered channel ch := make(chan int) // OR // Create a buffered channel with a capacity of 3 bufferedCh := make(chan int, 3) // Use the channels... }
Golang Buffered Channels vs Unbuffered Channels:
package main import "fmt" func main() { // Unbuffered channel unbufferedCh := make(chan int) // Buffered channel with a capacity of 3 bufferedCh := make(chan int, 3) // Use the channels... }
Using Channels for Communication in Golang:
package main import "fmt" func main() { ch := make(chan string) go func() { ch <- "Hello from goroutine!" }() msg := <-ch fmt.Println(msg) // Output: Hello from goroutine! }
Select Statement with Channels in Golang:
select
statement is used to wait on multiple communication operations. It blocks until one of its cases can execute.package main import ( "fmt" "time" ) func main() { ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(2 * time.Second) ch1 <- "Hello from ch1!" }() go func() { time.Sleep(1 * time.Second) ch2 <- "Hello from ch2!" }() select { case msg1 := <-ch1: fmt.Println(msg1) case msg2 := <-ch2: fmt.Println(msg2) } }
Closing Channels in Golang:
close
function to close a channel. A closed channel can no longer send values.package main import "fmt" func main() { ch := make(chan int) go func() { for i := 1; i <= 5; i++ { ch <- i } close(ch) }() for value := range ch { fmt.Println(value) } }
Channel Synchronization in Golang:
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup ch := make(chan int) // Goroutine 1 wg.Add(1) go func() { defer wg.Done() // Some work... ch <- 1 }() // Goroutine 2 wg.Add(1) go func() { defer wg.Done() // Some work... ch <- 2 }() // Wait for both goroutines to finish wg.Wait() close(ch) for value := range ch { fmt.Println(value) } }
Golang Channel Direction (Send and Receive):
package main import "fmt" func sendData(ch chan<- int, data int) { ch <- data } func main() { ch := make(chan int) go sendData(ch, 42) // Attempting to receive from a send-only channel will result in a compile-time error. // value := <-ch // Error }
Selecting from Multiple Channels in Golang:
select
statement to wait on multiple channels simultaneously.package main import ( "fmt" "time" ) func main() { ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(2 * time.Second) ch1 <- "Hello from ch1!" }() go func() { time.Sleep(1 * time.Second) ch2 <- "Hello from ch2!" }() select { case msg1 := <-ch1: fmt.Println(msg1) case msg2 := <-ch2: fmt.Println(msg2) } }