在 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 类型详解

  1. 无缓冲 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: 数据发送完成
    
  2. 有缓冲 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 的高级用法

  1. 使用 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
         }
     }
    }
    
  2. 关闭 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 - 接收到: %d\n", 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 - 接收到: %d\n", value)
     }
    }
    
  3. 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)

}

标签: none

添加新评论