Channels
Sure, channels are a powerful concurrency primitive in Go, used for communication between goroutines. Here are the common patterns for using channels in Go:
1. Basic Send and Receive
A simple pattern where one goroutine sends data on a channel and another goroutine receives it.
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
ch <- 42 // Send
}()
val := <-ch // Receive
fmt.Println(val)
}2. Buffered Channels
Buffered channels allow sending and receiving without requiring the other side to be ready at the same time.
package main
import (
"fmt"
)
func main() {
ch := make(chan int, 2) // Buffered channel
ch <- 1
ch <- 2
fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
}3. Range over Channels
Using a for loop to receive values from a channel until it is closed.
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}()
for val := range ch {
fmt.Println(val)
}
}4. Select Statement
The select statement lets a goroutine wait on multiple communication operations.
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "one"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("received", msg1)
case msg2 := <-ch2:
fmt.Println("received", msg2)
}
}
}5. Fan-out and Fan-in
Fan-out is a pattern where multiple goroutines receive from a single channel. Fan-in is a pattern where multiple goroutines send to a single channel.
Fan-out:
package main
import (
"fmt"
"sync"
)
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
results <- j * 2
fmt.Println("worker", id, "processed job", j)
}
}
func main() {
jobs := make(chan int, 10)
results := make(chan int, 10)
for w := 1; w < 4; w++ {
go worker(w, jobs, results)
}
for j := 1; j < 6; j++ {
jobs <- j
}
close(jobs)
for a := 1; a < 6; a++ {
fmt.Println("result", <-results)
}
}Fan-in:
package main
import (
"fmt"
"math/rand"
"time"
)
func producer(id int, ch chan<- int) {
for {
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
ch <- id
}
}
func main() {
ch := make(chan int)
for i := 1; i <= 3; i++ {
go producer(i, ch)
}
for {
fmt.Println(<-ch)
}
}6. Timeouts
Using select with time.After to implement timeouts.
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go func() {
time.Sleep(2 * time.Second)
ch <- 1
}()
select {
case val := <-ch:
fmt.Println("received", val)
case <-time.After(1 * time.Second):
fmt.Println("timeout")
}
}7. Closing Channels
Close a channel to indicate that no more values will be sent on it.
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}()
for val := range ch {
fmt.Println(val)
}
_, ok := <-ch
if !ok {
fmt.Println("channel closed")
}
}8. Pipeline
Connecting multiple stages of computation through channels, forming a pipeline.
package main
import (
"fmt"
)
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
func main() {
nums := gen(2, 3, 4)
squares := sq(nums)
for n := range squares {
fmt.Println(n)
}
}These patterns provide a solid foundation for effectively using channels in Go for various concurrency needs.