Go 语言 Channel(管道/通道)

一、Channel 概述

1.1 什么是 Channel

Channel(管道/通道) 是 Go 语言中用于 Goroutine 间通信的核心机制。它是一个类型化的消息队列,遵循**先进先出(FIFO)**的原则。

1.2 为什么需要 Channel

在并发编程中,Goroutine 之间需要通信和同步。Go 提供了两种方式:

方式 特点 Go 的推荐
共享内存(锁) 通过锁保护共享变量 次要选择
消息传递(Channel) 通过通信来共享内存 首选方式

Go 谚语"Don't communicate by sharing memory; share memory by communicating."

不要通过共享内存来通信,而应该通过通信来共享内存。

1.3 Channel 的特点

  • 类型安全:Channel 是类型化的,只能传递特定类型的数据
  • 并发安全:多个 Goroutine 可以安全地并发读写
  • 同步机制:可以用于 Goroutine 之间的同步
  • 阻塞特性:发送和接收操作可能会阻塞

二、Channel 的基本操作

2.1 创建 Channel

复制代码
// 语法:make(chan 类型, 容量)

// 1. 无缓冲 Channel(同步)
ch := make(chan int)

// 2. 有缓冲 Channel(异步)
ch := make(chan int, 5)  // 容量为 5

// 3. 只能接收的 Channel
var recvCh <-chan int = ch

// 4. 只能发送的 Channel
var sendCh chan<- int = ch

2.2 发送数据

复制代码
ch := make(chan int)

// 发送数据到 Channel
ch <- 42

// 语法:channel <- value

2.3 接收数据

复制代码
ch := make(chan int)

// 1. 接收数据
value := <-ch

// 2. 接收数据并检查 Channel 是否关闭
value, ok := <-ch
if ok {
    fmt.Println("Received:", value)
} else {
    fmt.Println("Channel closed")
}

// 语法:<-channel

2.4 关闭 Channel

复制代码
ch := make(chan int)

// 关闭 Channel
close(ch)

// ⚠️ 注意:
// 1. 只有发送者应该关闭 Channel
// 2. 向已关闭的 Channel 发送数据会 panic
// 3. 从已关闭的 Channel 接收数据会立即返回零值
// 4. 重复关闭 Channel 会 panic

2.5 完整示例

go 复制代码
package main

import "fmt"

func main() {
	// 创建 Channel
	ch := make(chan string)

	// 启动 Goroutine 发送数据
	go func() {
		ch <- "Hello, Channel!"
	}()

	// 接收数据
	msg := <-ch
	fmt.Println(msg) // 输出: Hello, Channel!
}

三、无缓冲 Channel vs 有缓冲 Channel

3.1 无缓冲 Channel(Unbuffered Channel)

特点
  • 同步通信:发送和接收必须同时准备好
  • 阻塞特性:发送操作会阻塞,直到有接收者;接收操作会阻塞,直到有发送者
  • 容量为 0:不能存储数据
工作原理
复制代码
发送者                    接收者
  |                        |
  |----发送数据---->等待    |
  |       阻塞             |
  |                  <--接收数据
  |       继续             |
  |                   继续  |
示例
go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int) // 无缓冲 Channel

	// 发送者
	go func() {
		fmt.Println("Sending...")
		ch <- 42 // 阻塞,直到有接收者
		fmt.Println("Sent!")
	}()

	// 模拟延迟
	time.Sleep(2 * time.Second)

	// 接收者
	fmt.Println("Receiving...")
	value := <-ch
	fmt.Printf("Received: %d\n", value)
}

输出

复制代码
Sending...
(等待 2 秒)
Receiving...
Received: 42
Sent!
使用场景
  • 同步操作:确保发送和接收同步进行
  • 信号通知:通知另一个 Goroutine 完成某个任务
  • 控制流程:精确控制 Goroutine 的执行顺序

3.2 有缓冲 Channel(Buffered Channel)

特点
  • 异步通信:发送者可以先发送,接收者稍后接收
  • 容量限制:可以存储固定数量的元素
  • 非阻塞(容量未满时):发送操作在缓冲区未满时不阻塞
工作原理
复制代码
发送者          缓冲区          接收者
  |         [_, _, _]          |
  |--发送--> [1, _, _]         |
  |--发送--> [1, 2, _]         |
  |--发送--> [1, 2, 3] (满了)  |
  |  阻塞                       |
  |         [2, 3, _] <--接收--|
  |--继续--> [2, 3, 4]         |
示例
go 复制代码
package main

import "fmt"

func main() {
	// 创建容量为 2 的缓冲 Channel
	ch := make(chan string, 2)

	// 发送数据(不会阻塞,因为缓冲区未满)
	ch <- "buffered"
	ch <- "channel"

	// 接收数据
	fmt.Println(<-ch) // 输出: buffered
	fmt.Println(<-ch) // 输出: channel
}
缓冲区满时的阻塞
go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int, 2) // 容量为 2

	// 发送者
	go func() {
		for i := 1; i <= 5; i++ {
			fmt.Printf("Sending %d...\n", i)
			ch <- i
			fmt.Printf("Sent %d\n", i)
		}
		close(ch)
	}()

	// 延迟接收
	time.Sleep(2 * time.Second)

	// 接收者
	for value := range ch {
		fmt.Printf("Received: %d\n", value)
		time.Sleep(500 * time.Millisecond)
	}
}

输出

复制代码
Sending 1...
Sent 1
Sending 2...
Sent 2
Sending 3...
(阻塞,等待接收者)
(2 秒后)
Received: 1
Sent 3
Sending 4...
Received: 2
Sent 4
Sending 5...
Received: 3
Sent 5
Received: 4
Received: 5
使用场景
  • 提高性能:减少 Goroutine 之间的同步开销
  • 削峰填谷:缓冲突发的数据流
  • 生产者-消费者模式:平衡生产和消费速度
  • Worker Pool:任务队列

3.3 对比总结

特性 无缓冲 Channel 有缓冲 Channel
创建方式 make(chan T) make(chan T, n)
容量 0 n(指定的容量)
同步性 同步通信 异步通信(缓冲区未满时)
发送阻塞 总是阻塞到有接收者 缓冲区满时阻塞
接收阻塞 总是阻塞到有发送者 缓冲区空时阻塞
使用场景 同步、信号通知 异步、削峰填谷

四、Channel 的方向

4.1 双向 Channel(默认)

复制代码
ch := make(chan int)  // 既能发送也能接收

4.2 单向 Channel

只能发送的 Channel
复制代码
var sendOnly chan<- int

func send(ch chan<- int) {
    ch <- 42  // 只能发送
    // value := <-ch  // 编译错误!不能接收
}
只能接收的 Channel
复制代码
var recvOnly <-chan int

func receive(ch <-chan int) {
    value := <-ch  // 只能接收
    // ch <- 42  // 编译错误!不能发送
}

4.3 Channel 方向转换

双向 Channel 可以隐式转换为单向 Channel,反之不行:

go 复制代码
package main

import "fmt"

// 生产者:只能发送
func producer(ch chan<- int) {
	for i := 0; i < 5; i++ {
		ch <- i
	}
	close(ch)
}

// 消费者:只能接收
func consumer(ch <-chan int) {
	for value := range ch {
		fmt.Println("Received:", value)
	}
}

func main() {
	ch := make(chan int) // 双向 Channel

	go producer(ch) // 隐式转换为只发送
	consumer(ch)    // 隐式转换为只接收
}

4.4 为什么需要单向 Channel?

  1. 类型安全:防止误用(如在接收端发送数据)
  2. 接口清晰:明确函数的意图
  3. 编译时检查:在编译时发现错误

五、关闭 Channel

5.1 关闭的语法

复制代码
ch := make(chan int)
close(ch)

5.2 关闭的规则

✅ 正确做法
复制代码
// 1. 只有发送者应该关闭 Channel
func sender(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch)  // 发送者关闭
}

// 2. 接收者不应该关闭 Channel
func receiver(ch <-chan int) {
    for value := range ch {
        fmt.Println(value)
    }
    // 不要在这里关闭!
}
❌ 错误做法
复制代码
ch := make(chan int)

// 错误 1:向已关闭的 Channel 发送数据
close(ch)
ch <- 1  // panic: send on closed channel

// 错误 2:重复关闭 Channel
close(ch)
close(ch)  // panic: close of closed channel

// 错误 3:关闭 nil Channel
var ch chan int
close(ch)  // panic: close of nil channel

5.3 检查 Channel 是否关闭

go 复制代码
package main

import "fmt"

func main() {
	ch := make(chan int, 2)

	ch <- 1
	ch <- 2
	close(ch)

	// 方法 1:使用双返回值
	value, ok := <-ch
	fmt.Printf("Value: %d, Open: %v\n", value, ok) // 1, true

	value, ok = <-ch
	fmt.Printf("Value: %d, Open: %v\n", value, ok) // 2, true

	value, ok = <-ch
	fmt.Printf("Value: %d, Open: %v\n", value, ok) // 0, false

	// 方法 2:使用 range(推荐)
	ch2 := make(chan int, 2)
	ch2 <- 10
	ch2 <- 20
	close(ch2)

	for value := range ch2 {
		fmt.Println("Range:", value)
	}
}

5.4 关闭 Channel 的时机

复制代码
// 场景 1:数据发送完毕
func sendData(ch chan<- int) {
    for i := 0; i < 10; i++ {
        ch <- i
    }
    close(ch)  // 发送完毕,关闭 Channel
}

// 场景 2:通知所有接收者停止
func broadcast(done chan struct{}) {
    // 执行某些操作...
    close(done)  // 关闭 Channel,通知所有等待的 Goroutine
}

// 场景 3:不需要关闭(由 GC 回收)
func noClose() {
    ch := make(chan int)
    // 如果没有人再使用 ch,可以不关闭
    // GC 会自动回收
}

六、select 语句

6.1 基本概念

select 语句用于在多个 Channel 操作中进行选择,类似于 switch,但用于 Channel。

6.2 基本语法

复制代码
select {
case <-ch1:
    // 从 ch1 接收到数据
case ch2 <- value:
    // 向 ch2 发送数据
case value := <-ch3:
    // 从 ch3 接收数据并使用
default:
    // 如果所有 case 都阻塞,执行 default
}

6.3 基本使用

复制代码
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    // Goroutine 1
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "from ch1"
    }()
    
    // Goroutine 2
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "from ch2"
    }()
    
    // 使用 select 等待第一个完成的操作
    select {
    case msg1 := <-ch1:
        fmt.Println("Received:", msg1)
    case msg2 := <-ch2:
        fmt.Println("Received:", msg2)
    }
}

6.4 select 的特性

1. 随机选择

当多个 case 同时就绪时,select 会随机选择一个执行:

go 复制代码
package main

import "fmt"

func main() {
	ch := make(chan int, 2)
	ch <- 1
	ch <- 2

	// 两个 case 都就绪,随机选择
	select {
	case val := <-ch:
		fmt.Println("Case 1:", val)
	case val := <-ch:
		fmt.Println("Case 2:", val)
	}
}
2. default 分支(非阻塞)
go 复制代码
package main

import "fmt"

func main() {
	ch := make(chan int)

	select {
	case val := <-ch:
		fmt.Println("Received:", val)
	default:
		fmt.Println("No data available") // 立即执行
	}
}
3. 超时处理
go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan string)

	go func() {
		time.Sleep(2 * time.Second)
		ch <- "result"
	}()

	select {
	case result := <-ch:
		fmt.Println("Got result:", result)
	case <-time.After(1 * time.Second):
		fmt.Println("Timeout!")
	}
}
4. 配合 Context 使用
go 复制代码
package main

import (
	"context"
	"fmt"
	"time"
)

func worker(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("Worker stopped")
			return
		default:
			fmt.Println("Working...")
			time.Sleep(500 * time.Millisecond)
		}
	}
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	go worker(ctx)

	time.Sleep(3 * time.Second)
}

6.5 select 的常见模式

模式 1:超时控制
复制代码
func fetchWithTimeout(url string) (string, error) {
    result := make(chan string)
    
    go func() {
        // 模拟网络请求
        time.Sleep(3 * time.Second)
        result <- "data"
    }()
    
    select {
    case data := <-result:
        return data, nil
    case <-time.After(2 * time.Second):
        return "", fmt.Errorf("timeout")
    }
}
模式 2:心跳检测
复制代码
func heartbeat(interval time.Duration) {
    ticker := time.NewTicker(interval)
    defer ticker.Stop()
    
    for {
        select {
        case <-ticker.C:
            fmt.Println("Heartbeat...")
        }
    }
}
模式 3:退出信号
复制代码
func worker(done <-chan bool) {
    for {
        select {
        case <-done:
            fmt.Println("Exiting...")
            return
        default:
            // 执行工作
            time.Sleep(100 * time.Millisecond)
        }
    }
}

七、常见模式与实战

7.1 生产者-消费者模式

go 复制代码
package main

import (
	"fmt"
	"time"
)

// 生产者
func producer(ch chan<- int, id int) {
	for i := 0; i < 5; i++ {
		value := id*10 + i
		fmt.Printf("Producer %d: producing %d\n", id, value)
		ch <- value
		time.Sleep(100 * time.Millisecond)
	}
}

// 消费者
func consumer(ch <-chan int, id int) {
	for value := range ch {
		fmt.Printf("Consumer %d: consumed %d\n", id, value)
		time.Sleep(200 * time.Millisecond)
	}
}

func main() {
	ch := make(chan int, 10)

	// 启动 2 个生产者
	go producer(ch, 1)
	go producer(ch, 2)

	// 启动 3 个消费者
	go consumer(ch, 1)
	go consumer(ch, 2)
	go consumer(ch, 3)

	time.Sleep(5 * time.Second)
	close(ch)
	time.Sleep(1 * time.Second)
}

7.2 Worker Pool(工作池)

go 复制代码
package main

import (
	"fmt"
	"sync"
	"time"
)

type Job struct {
	ID int
}

type Result struct {
	JobID int
	Value int
}

// Worker 函数
func worker(id int, jobs <-chan Job, results chan<- Result, wg *sync.WaitGroup) {
	defer wg.Done()

	for job := range jobs {
		fmt.Printf("Worker %d processing job %d\n", id, job.ID)
		time.Sleep(100 * time.Millisecond) // 模拟工作

		results <- Result{
			JobID: job.ID,
			Value: job.ID * 2,
		}
	}
}

func main() {
	const numWorkers = 3
	const numJobs = 10

	jobs := make(chan Job, numJobs)
	results := make(chan Result, numJobs)
	var wg sync.WaitGroup

	// 启动 worker
	for w := 1; w <= numWorkers; w++ {
		wg.Add(1)
		go worker(w, jobs, results, &wg)
	}

	// 发送任务
	for j := 1; j <= numJobs; j++ {
		jobs <- Job{ID: j}
	}
	close(jobs)

	// 等待所有 worker 完成
	go func() {
		wg.Wait()
		close(results)
	}()

	// 收集结果
	for result := range results {
		fmt.Printf("Job %d result: %d\n", result.JobID, result.Value)
	}
}

7.3 扇入(Fan-In)模式

将多个输入 Channel 合并到一个输出 Channel:

go 复制代码
package main

import (
	"fmt"
	"sync"
	"time"
)

func producer(id int, ch chan<- int) {
	for i := 0; i < 5; i++ {
		ch <- id*10 + i
		time.Sleep(100 * time.Millisecond)
	}
	close(ch)
}

func fanIn(channels ...<-chan int) <-chan int {
	out := make(chan int)
	var wg sync.WaitGroup

	// 为每个输入 Channel 启动一个 Goroutine
	for _, ch := range channels {
		wg.Add(1)
		go func(c <-chan int) {
			defer wg.Done()
			for value := range c {
				out <- value
			}
		}(ch)
	}

	// 等待所有输入 Channel 关闭后,关闭输出 Channel
	go func() {
		wg.Wait()
		close(out)
	}()

	return out
}

func main() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	ch3 := make(chan int)

	go producer(1, ch1)
	go producer(2, ch2)
	go producer(3, ch3)

	// 合并所有 Channel
	merged := fanIn(ch1, ch2, ch3)

	// 接收合并后的数据
	for value := range merged {
		fmt.Println("Received:", value)
	}
}

7.4 扇出(Fan-Out)模式

将一个输入 Channel 分发到多个输出 Channel:

go 复制代码
package main

import (
	"fmt"
	"sync"
)

func producer(ch chan<- int) {
	for i := 0; i < 10; i++ {
		ch <- i
	}
	close(ch)
}

func fanOut(in <-chan int, n int) []<-chan int {
	outs := make([]<-chan int, n)

	for i := 0; i < n; i++ {
		ch := make(chan int)
		outs[i] = ch

		go func(out chan<- int, id int) {
			for value := range in {
				fmt.Printf("Worker %d received: %d\n", id, value)
				out <- value * 2
			}
			close(out)
		}(ch, i)
	}

	return outs
}

func main() {
	input := make(chan int)

	go producer(input)

	// 分发到 3 个 worker
	outputs := fanOut(input, 3)

	// 收集结果
	var wg sync.WaitGroup
	for i, out := range outputs {
		wg.Add(1)
		go func(ch <-chan int, id int) {
			defer wg.Done()
			for value := range ch {
				fmt.Printf("Output %d: %d\n", id, value)
			}
		}(out, i)
	}

	wg.Wait()
}

7.5 Pipeline(管道)模式

go 复制代码
package main

import "fmt"

// 阶段 1:生成数字
func generator(nums ...int) <-chan int {
	out := make(chan int)
	go func() {
		for _, n := range nums {
			out <- n
		}
		close(out)
	}()
	return out
}

// 阶段 2:平方
func square(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		for n := range in {
			out <- n * n
		}
		close(out)
	}()
	return out
}

// 阶段 3:加倍
func double(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		for n := range in {
			out <- n * 2
		}
		close(out)
	}()
	return out
}

func main() {
	// 构建 pipeline
	nums := generator(1, 2, 3, 4, 5)
	squared := square(nums)
	doubled := double(squared)

	// 消费结果
	for result := range doubled {
		fmt.Println(result)
	}
}

7.6 限流器(Rate Limiter)

go 复制代码
package main

import (
	"fmt"
	"time"
)

func rateLimiter(rate time.Duration, burst int) chan struct{} {
	limiter := make(chan struct{}, burst)

	// 填充初始令牌
	for i := 0; i < burst; i++ {
		limiter <- struct{}{}
	}

	// 定期添加令牌
	go func() {
		ticker := time.NewTicker(rate)
		defer ticker.Stop()

		for range ticker.C {
			select {
			case limiter <- struct{}{}:
			default:
				// 令牌桶满了,丢弃
			}
		}
	}()

	return limiter
}

func main() {
	// 每 500ms 一个令牌,最多 3 个令牌
	limiter := rateLimiter(500*time.Millisecond, 3)

	for i := 1; i <= 10; i++ {
		<-limiter // 获取令牌(可能阻塞)
		fmt.Printf("Request %d at %v\n", i, time.Now())
	}
}

八、Channel 的高级特性

8.1 nil Channel

复制代码
var ch chan int  // nil Channel

// 向 nil Channel 发送数据:永久阻塞
ch <- 1  // 永久阻塞

// 从 nil Channel 接收数据:永久阻塞
<-ch  // 永久阻塞

// 关闭 nil Channel:panic
close(ch)  // panic
nil Channel 的应用
复制代码
// 禁用某个 case
func example() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    
    // 禁用 ch2
    ch2 = nil
    
    select {
    case val := <-ch1:
        fmt.Println("ch1:", val)
    case val := <-ch2:  // 永不执行(ch2 是 nil)
        fmt.Println("ch2:", val)
    }
}

8.2 for-range 遍历 Channel

复制代码
ch := make(chan int, 5)

// 发送数据
go func() {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch)  // 必须关闭,否则 range 会永久阻塞
}()

// 遍历 Channel
for value := range ch {
    fmt.Println(value)
}

8.3 len 和 cap

复制代码
ch := make(chan int, 5)

ch <- 1
ch <- 2
ch <- 3

fmt.Println("len:", len(ch))  // 3(当前元素数量)
fmt.Println("cap:", cap(ch))  // 5(容量)

⚠️ 注意 :不要依赖 lencap 来做决策,因为它们的值可能在检查和使用之间发生变化(竞态条件)。


九、最佳实践

9.1 Do's(推荐做法)

✅ 1. 由发送者关闭 Channel
复制代码
func producer(ch chan<- int) {
    for i := 0; i < 10; i++ {
        ch <- i
    }
    close(ch)  // 发送者关闭
}
✅ 2. 使用 range 遍历 Channel
复制代码
for value := range ch {
    fmt.Println(value)
}
✅ 3. 使用单向 Channel 明确意图
复制代码
func send(ch chan<- int) {
    ch <- 42
}

func receive(ch <-chan int) {
    value := <-ch
}
✅ 4. 使用 select 实现超时
复制代码
select {
case result := <-ch:
    // 处理结果
case <-time.After(5 * time.Second):
    // 超时处理
}
✅ 5. 使用 Context 控制生命周期
复制代码
func worker(ctx context.Context, ch <-chan int) {
    for {
        select {
        case <-ctx.Done():
            return
        case value := <-ch:
            // 处理数据
        }
    }
}

9.2 Don'ts(禁止做法)

❌ 1. 不要向已关闭的 Channel 发送数据
复制代码
close(ch)
ch <- 1  // panic!
❌ 2. 不要重复关闭 Channel
复制代码
close(ch)
close(ch)  // panic!
❌ 3. 不要从接收端关闭 Channel
复制代码
// 错误
func consumer(ch <-chan int) {
    for value := range ch {
        fmt.Println(value)
    }
    close(ch)  // 错误!接收者不应该关闭
}
❌ 4. 不要忽略 Channel 的关闭检查
复制代码
// 不好
value := <-ch

// 好
value, ok := <-ch
if !ok {
    // Channel 已关闭
}
❌ 5. 不要滥用缓冲 Channel
复制代码
// 不好:缓冲区过大
ch := make(chan int, 1000000)

// 好:根据实际需求设置合理的大小
ch := make(chan int, 10)

十、常见问题与解答

10.1 为什么会发生死锁?

复制代码
// 死锁示例
func main() {
    ch := make(chan int)
    ch <- 1  // 阻塞,因为没有接收者
    fmt.Println(<-ch)  // 永不执行
}

解决方案

  1. 使用 Goroutine
  2. 使用缓冲 Channel

10.2 如何安全地关闭 Channel?

原则:只有发送者关闭 Channel

多个发送者场景

复制代码
// 使用额外的 Channel 通知所有发送者停止
func example() {
    ch := make(chan int)
    done := make(chan struct{})
    
    // 多个发送者
    for i := 0; i < 3; i++ {
        go func(id int) {
            for {
                select {
                case <-done:
                    return
                case ch <- id:
                }
            }
        }(i)
    }
    
    // 通知所有发送者停止
    close(done)
}

10.3 Channel 会导致内存泄漏吗?

可能会,如果:

  • Goroutine 永久阻塞在 Channel 操作上
  • Channel 没有被正确关闭和回收

预防方法

  • 使用 Context 控制生命周期
  • 使用超时机制
  • 确保 Goroutine 能够退出

十一、性能与调优

11.1 Channel 的性能特点

操作 性能
无缓冲 Channel 需要同步,较慢
有缓冲 Channel 缓冲区未满时较快
select 语句 有一定开销
锁(Mutex) 通常比 Channel 快

11.2 何时使用 Channel,何时使用锁?

场景 推荐
Goroutine 间通信 Channel
数据传递 Channel
保护共享状态
简单的计数器 atomic 或锁
复杂的协调 Channel + select

11.3 优化建议

  1. 合理设置缓冲区大小
  2. 避免频繁创建 Channel
  3. 使用 sync.Pool 复用对象
  4. 使用 atomic 替代简单的 Channel 计数

十二、总结

12.1 核心要点

概念 说明
Channel Go 中 Goroutine 间通信的首选方式
无缓冲 同步通信,发送和接收同时准备好
有缓冲 异步通信,缓冲区未满时不阻塞
单向 Channel 提高类型安全性,明确意图
close 只由发送者关闭
select 多路复用,实现超时、取消等功能

12.2 使用建议

  1. 优先使用 Channel 而非共享内存
  2. 由发送者关闭 Channel
  3. 使用 select 实现超时和取消
  4. 使用单向 Channel 提高安全性
  5. 合理设置缓冲区大小
  6. 配合 Context 使用更优雅

12.3 Go 谚语

"Don't communicate by sharing memory; share memory by communicating."

Channel 是 Go 并发编程的灵魂,掌握 Channel 就掌握了 Go 并发的精髓。

相关推荐
码界筑梦坊2 小时前
113-基于Python的国际超市电商销售数据可视化分析系统
开发语言·python·信息可视化·毕业设计·fastapi
初心未改HD2 小时前
Go语言结构体Struct:内存布局、标签、接收者与内存对齐
开发语言·golang
lsx2024062 小时前
JavaScript 类
开发语言
默 语3 小时前
基于 Spring Boot 3 + LangChain4j 快速构建企业级 AI 应用实战
人工智能·spring boot·后端
jieyucx3 小时前
Go 数据结构入门:线性表、顺序表、链表
数据结构·链表·golang
专科3年的修炼3 小时前
uni-app移动应用开发第四章
开发语言·javascript·uni-app
码界筑梦坊3 小时前
114-基于Python的1688电脑硬件数据可视化分析系统
开发语言·python·信息可视化·数据分析·毕业设计·echarts·数据可视化
DXM05213 小时前
第2期:0配置!10分钟搭建ArcGIS Python开发环境(无需装VS)
开发语言·人工智能·python·arcgis·arcgis自动化
薪火铺子3 小时前
SpringBoot WebServer启动与监听器原理深度解析
spring boot·后端·tomcat