Skip to content

channels.go

文件信息

  • 📄 原文件:02_channels.go
  • 🔤 语言:go

完整代码

go
package main

import (
	"fmt"
	"time"
)

// ============================================================
//                      Channel 通道
// ============================================================
// Channel 是 goroutine 之间通信的管道
// Go 的并发哲学:不要通过共享内存来通信,而要通过通信来共享内存

func main02() {
	fmt.Println("\n==================== 02_channels ====================")
	fmt.Println("=== Channel 基础 ===")

	// ----------------------------------------------------------
	// 创建 Channel
	// ----------------------------------------------------------
	// 无缓冲 channel:发送和接收必须同步
	ch1 := make(chan int)

	// 有缓冲 channel:缓冲区满时才阻塞
	ch2 := make(chan string, 3)

	fmt.Printf("无缓冲 channel: %T\n", ch1)
	fmt.Printf("有缓冲 channel: %T, 容量=%d\n", ch2, cap(ch2))

	// ----------------------------------------------------------
	// 发送和接收
	// ----------------------------------------------------------
	fmt.Println("\n=== 发送和接收 ===")

	// 必须在不同 goroutine 中操作无缓冲 channel
	go func() {
		ch1 <- 42 // 发送
	}()

	value := <-ch1 // 接收
	fmt.Println("收到:", value)

	// 有缓冲 channel 可以在同一个 goroutine 中操作
	ch2 <- "A"
	ch2 <- "B"
	fmt.Println("缓冲 channel 收到:", <-ch2, <-ch2)

	// ----------------------------------------------------------
	// Channel 方向
	// ----------------------------------------------------------
	fmt.Println("\n=== Channel 方向 ===")

	ch := make(chan int, 1)

	// 只写 channel
	go sendOnly(ch)

	// 只读 channel
	go receiveOnly(ch)

	time.Sleep(50 * time.Millisecond)

	// ----------------------------------------------------------
	// 关闭 Channel
	// ----------------------------------------------------------
	fmt.Println("\n=== 关闭 Channel ===")

	dataCh := make(chan int, 5)

	// 发送数据后关闭
	go func() {
		for i := 1; i <= 5; i++ {
			dataCh <- i
		}
		close(dataCh) // 关闭 channel
	}()

	// 接收数据
	for {
		v, ok := <-dataCh
		if !ok {
			fmt.Println("Channel 已关闭")
			break
		}
		fmt.Println("收到:", v)
	}

	// ----------------------------------------------------------
	// range 遍历 Channel
	// ----------------------------------------------------------
	fmt.Println("\n=== range 遍历 Channel ===")

	numCh := make(chan int, 3)

	go func() {
		for i := 10; i <= 30; i += 10 {
			numCh <- i
		}
		close(numCh)
	}()

	// range 会自动检测 channel 关闭
	for num := range numCh {
		fmt.Println("range 收到:", num)
	}

	// ----------------------------------------------------------
	// Select 多路复用
	// ----------------------------------------------------------
	fmt.Println("\n=== Select 多路复用 ===")

	ch1 = make(chan int)
	ch2New := make(chan string)

	go func() {
		time.Sleep(30 * time.Millisecond)
		ch1 <- 100
	}()

	go func() {
		time.Sleep(20 * time.Millisecond)
		ch2New <- "hello"
	}()

	// select 等待多个 channel
	for i := 0; i < 2; i++ {
		select {
		case v := <-ch1:
			fmt.Println("从 ch1 收到:", v)
		case v := <-ch2New:
			fmt.Println("从 ch2 收到:", v)
		}
	}

	// ----------------------------------------------------------
	// Select 超时处理
	// ----------------------------------------------------------
	fmt.Println("\n=== Select 超时 ===")

	slowCh := make(chan int)

	go func() {
		time.Sleep(200 * time.Millisecond)
		slowCh <- 999
	}()

	select {
	case v := <-slowCh:
		fmt.Println("收到:", v)
	case <-time.After(100 * time.Millisecond):
		fmt.Println("超时!")
	}

	// ----------------------------------------------------------
	// Select default(非阻塞)
	// ----------------------------------------------------------
	fmt.Println("\n=== Select default ===")

	emptyCh := make(chan int)

	select {
	case v := <-emptyCh:
		fmt.Println("收到:", v)
	default:
		fmt.Println("没有数据可读,继续执行")
	}

	// 非阻塞发送
	fullCh := make(chan int, 1)
	fullCh <- 1 // 缓冲区满

	select {
	case fullCh <- 2:
		fmt.Println("发送成功")
	default:
		fmt.Println("缓冲区满,发送失败")
	}

	// ----------------------------------------------------------
	// 单向 Channel 转换
	// ----------------------------------------------------------
	fmt.Println("\n=== 单向 Channel ===")

	biCh := make(chan int, 1)

	// 双向可以赋值给单向
	var sendCh chan<- int = biCh // 只写
	var recvCh <-chan int = biCh // 只读

	sendCh <- 42
	fmt.Println("单向接收:", <-recvCh)

	// ----------------------------------------------------------
	// nil Channel
	// ----------------------------------------------------------
	fmt.Println("\n=== nil Channel ===")

	var nilCh chan int

	// nil channel 永远阻塞
	// <-nilCh   // 永远阻塞
	// nilCh <- 1 // 永远阻塞

	// 在 select 中可以用 nil 禁用某个 case
	select {
	case <-nilCh:
		fmt.Println("不会执行")
	default:
		fmt.Println("nil channel 被跳过")
	}
}

// ----------------------------------------------------------
// 单向 Channel 函数参数
// ----------------------------------------------------------

// 只能发送
func sendOnly(ch chan<- int) {
	ch <- 100
	fmt.Println("sendOnly: 发送完成")
}

// 只能接收
func receiveOnly(ch <-chan int) {
	v := <-ch
	fmt.Println("receiveOnly: 收到", v)
}

// ============================================================
//                      重要注意事项
// ============================================================
//
// 1. 【创建 Channel】
//    make(chan Type)      // 无缓冲
//    make(chan Type, n)   // 有缓冲,容量 n
//
// 2. 【无缓冲 vs 有缓冲】
//    无缓冲:同步通信,发送阻塞直到有人接收
//    有缓冲:异步通信,缓冲区满时才阻塞
//
// 3. 【关闭 Channel】
//    - 只有发送方应该关闭
//    - 关闭后不能再发送(panic)
//    - 关闭后可以继续接收,返回零值
//    - 重复关闭会 panic
//
// 4. 【检测关闭】
//    v, ok := <-ch  // ok 为 false 表示已关闭
//    for v := range ch  // 自动检测关闭
//
// 5. 【select 规则】
//    - 多个 case 就绪时,随机选择一个
//    - 没有 case 就绪且有 default,执行 default
//    - 没有 case 就绪且无 default,阻塞
//
// 6. 【常见模式】
//    - 超时: select + time.After
//    - 退出: select + done channel
//    - 非阻塞: select + default
//
// 7. 【死锁注意】
//    - 无缓冲 channel 在同一 goroutine 中发送和接收会死锁
//    - 所有 goroutine 都阻塞时程序会 panic

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布