主页

在 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: 数据发送完成
  1. 有缓冲 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
        }
    }
}
  1. 关闭 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)
    }
}
  1. 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)
}

版权属于:三分快乐,七纷幸福
作品采用:本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
0
查看目录

目录

来自 《深入理解 Go 语言 Channel:通信与同步的艺术》
评论

三分快乐,七纷幸福
114 文章数
8 评论量
11 分类数
117 页面数
已在风雨中度过 3年198天7小时45分