别着急,坐和放宽
使用社交账号登录
Go 的并发模型核心在于 CSP (Communicating Sequential Processes)。其基本哲学是:通过通信来共享内存,而不是通过共享内存来通信。 Channel 是实现这一模型的关键纽带,负责在不同的 Goroutine 之间传递消息并实现同步。
传统并发模型(如 Java/C++)依赖共享内存与互斥锁(Mutex)。这种方式存在以下痛点:
Channel 模型优势:
hchan 结构分析Channel 并非完全无锁,而是通过 Go 运行时管理的 hchan 结构体实现。其核心组件包括:
hchan 结构体本身,确保发送/接收操作的原子性。锁粒度极小。在函数参数中使用单向 Channel 可增强代码的安全性与意图表达:
chan<- int:只写 Channel。<-chan int:只读 Channel。make(chan int)):发送与接收必须同步发生。若无接收者,发送方阻塞;反之亦然。适用于强同步场景。make(chan int, cap)):缓冲区未满时,发送操作异步完成;缓冲区有数据时,接收操作异步完成。适用于解耦生产者与消费者的速度差异。| 操作 | nil Channel | 已关闭 Channel | 正常 Channel |
|---|---|---|---|
发送 ch <- v | 永久阻塞 | Panic | 成功或阻塞 |
接收 <-ch | 永久阻塞 | 返回零值 + false | 成功或阻塞 |
关闭 close(ch) | Panic | Panic | 成功 |
关闭原则:始终由发送方负责关闭 Channel。严禁在接收方或多个发送方场景下随意关闭,以防 Panic。
select 语句用于监控多个 Channel 操作,是 Go 异步 IO 与并发控制的核心。
select 会通过伪随机算法选择一个执行。default,当前 Goroutine 进入阻塞状态。A. 超时控制
利用 time.After 防止操作无限期挂起。
B. 非阻塞操作 (Default)
通过 default 分支实现即时返回。
C. 优雅退出 (Done Channel)
配合 context.Context 响应取消信号。
D. 多路复用 (Multiplexing) 同时监听多个数据源。
E. 优先级模拟
由于 select 的随机性,可以通过嵌套 select 模拟优先级。
Channel 提供了比原始锁更高级的抽象。通过 select 的多路复用能力,可以构建复杂的异步处理流水线。理解其底层的锁机制与内存拷贝特性,有助于在高性能场景下做出正确的工程决策。
type hchan struct {
// chan 里元素数量
qcount uint
// chan 底层循环数组的长度
dataqsiz uint
// 指向底层循环数组的指针
// 只针对有缓冲的 channel
buf unsafe.Pointer
// chan 中元素大小
elemsize uint16
// chan 是否被关闭的标志
closed uint32
// chan 中元素类型
elemtype *_type // element type
// 已发送元素在循环数组中的索引
sendx uint // send index
// 已接收元素在循环数组中的索引
recvx uint // receive index
// 等待接收的 goroutine 队列
recvq waitq // list of recv waiters
// 等待发送的 goroutine 队列
sendq waitq // list of send waiters
// 保护 hchan 中所有字段
lock mutex
}
select {
case res := <-resultCh:
handle(res)
case <-time.After(3 * time.Second):
log.Println("operation timed out")
}
select {
case data := <-ch:
process(data)
default:
// 如果 ch 无数据,不阻塞,直接走这里
}
for {
select {
case task := <-taskCh:
execute(task)
case <-ctx.Done():
// 释放资源并退出
return
}
}
select {
case msg := <-highPriorityCh:
handleHigh(msg)
case msg := <-lowPriorityCh:
handleLow(msg)
}
for {
select {
case msg := <-highPriorityCh:
handleHigh(msg)
default:
select {
case msg := <-highPriorityCh:
handleHigh(msg)
case msg := <-lowPriorityCh:
handleLow(msg)
case <-ctx.Done():
return
}
}
}