在 Go 语言中,channel 是一种强大的并发原语,它允许不同的 goroutine 之间进行通信和同步。今天我们就来深入探讨 channel 的各个方面,并通过丰富的实例来展示其强大功能。
什么是 Channel?
Channel 是 Go 语言中实现 CSP(Communicating Sequential Processes)并发模型的核心数据结构。它类似于一个类型化的管道,可以在 goroutine 之间安全地传递数据。
Channel 的基本操作
创建 Channel
go // 无缓冲 channel ch1 := make(chan int) // 有缓冲 channel,容量为 10 ch2 := make(chan int, 10) // 只读 channel var readOnly <-chan int = ch1 // 只写 channel var writeOnly chan<- int = ch1
发送和接收数据
go ch := make(chan int, 1) // 发送数据 ch <- 42 // 接收数据 value := <-ch fmt.Println(value) // 输出: 42
Channel 类型详解
- 无缓冲 Channel
无缓冲 channel 是同步的,发送和接收操作会阻塞,直到另一端准备好。
func unbufferedChannelDemo() { ch := make(chan string) go func() { fmt.Println("Goroutine: 准备发送数据") ch <- "Hello from goroutine!" fmt.Println("Goroutine: 数据发送完成") }() time.Sleep(1 * time.Second) fmt.Println("Main: 准备接收数据") msg := <-ch fmt.Println("Main: 接收到数据:", msg) time.Sleep(1 * time.Second) }
输出:
Goroutine: 准备发送数据 Main: 准备接收数据 Main: 接收到数据: Hello from goroutine! Goroutine: 数据发送完成
- 有缓冲 Channel
有缓冲 channel 是异步的,只有在缓冲区满时发送才会阻塞,在缓冲区空时接收才会阻塞。
func bufferedChannelDemo() { ch := make(chan int, 3) // 发送数据不会阻塞,因为缓冲区有空间 ch <- 1 ch <- 2 ch <- 3 fmt.Println("已发送 3 个数据到缓冲区") // 如果再发送就会阻塞,因为缓冲区已满 // ch <- 4 // 这行会阻塞 // 接收数据 fmt.Println("接收数据:", <-ch) // 1 fmt.Println("接收数据:", <-ch) // 2 fmt.Println("接收数据:", <-ch) // 3 }
Channel 的高级用法
- 使用 select 处理多个 Channel
func selectDemo() { ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(2 * time.Second) ch1 <- "来自 ch1 的消息" }() go func() { time.Sleep(1 * time.Second) ch2 <- "来自 ch2 的消息" }() for i := 0; i < 2; i++ { select { case msg1 := <-ch1: fmt.Println("接收到:", msg1) case msg2 := <-ch2: fmt.Println("接收到:", msg2) case <-time.After(3 * time.Second): fmt.Println("超时!") return } } }
- 关闭 Channel 和 range 循环
func closeChannelDemo() { ch := make(chan int, 5) // 生产者 go func() { for i := 0; i < 5; i++ { ch <- i } close(ch) // 关闭 channel }() // 消费者 - 方法1: 使用 ok 判断 for { if value, ok := <-ch; ok { fmt.Printf("方法1 - 接收到: %dn", value) } else { fmt.Println("Channel 已关闭") break } } // 消费者 - 方法2: 使用 range ch2 := make(chan int, 3) go func() { for i := 10; i < 13; i++ { ch2 <- i } close(ch2) }() for value := range ch2 { fmt.Printf("方法2 - 接收到: %dn", value) } }
- Channel 作为函数参数
// 只允许从 channel 读取 func reader(ch <-chan int) { for value := range ch { fmt.Println("读取:", value) } } // 只允许向 channel 写入 func writer(ch chan<- int, numbers []int) { for _, num := range numbers { ch <- num } close(ch) } func channelAsParameter() { ch := make(chan int, 3) numbers := []int{1, 2, 3, 4, 5} go writer(ch, numbers) reader(ch) }