通道
Channel
channel 是 goroutine 之间通信的管道,「不要通过共享内存来通信,而要通过通信来共享内存」正是 Go 并发的核心理念。发送方 ch <- v,接收方 v := <-ch,同一时间只有一个 goroutine 能访问数据,天然避免了数据竞争。
channel需要make初始化分配内存make初始化内存时可以设定缓存空间,在缓存空间被占用完之前都不会阻塞通道写入channel := make(chan int, len)- 有缓存的可以在协程外将数据写入缓存区
- 缓存区数据写满后,再次写入会陷入阻塞,但是程序编译不会报错
make初始化可以不设置缓存空间,表示通道处理同步消息:在通道数据被读取前都会阻塞通道写入channel := make(chan int)- 无缓存通道数据的读写都必须在
go协程中处理
package main
import (
"fmt"
"sync"
)
var (
wg sync.WaitGroup
ch = make(chan int, 11)
)
func main() {
wg.Add(2)
go write()
go read()
wg.Wait()
}
func write() {
defer wg.Done()
for i := 0; i < 10; i++ {
ch <- i
}
defer close(ch) // 在写入完成后关闭通道
}
func read() {
defer wg.Done()
for a := range ch { // 使用 range 语法读取通道
fmt.Println("a: ", a)
}
fmt.Println("close")
}
无缓冲通道与 select
无缓冲通道在发送和接收之间建立同步点:ch <- v 会阻塞到有接收方取走数据为止,适合两个 goroutine 间的「握手」。
select 语句可以同时等待多个通道操作,哪个就绪执行哪个(多个同时就绪时随机选择),配合 time.After 可以实现超时控制:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string) // 无缓冲通道
go func() {
time.Sleep(1 * time.Second)
ch <- "block synced"
}()
select {
case msg := <-ch:
fmt.Println("收到:", msg)
case <-time.After(2 * time.Second):
fmt.Println("超时退出")
}
}
输出:
收到: block synced
如果 goroutine 中 sleep 的时间超过 2 秒,则会走到超时分支打印「超时退出」。这是 Web3 监听链上事件时常用的模式——用 select + 超时避免 RPC 无响应导致程序永久阻塞。
Channel 数据读取

读数据
- 从空通道读取数据-->阻塞
- 从
nil通道读取数据-->阻塞 - 从有数据通道读取数据-->返回数据
- 从 关闭的通道读取数据-->可以读取缓存区的数据,缓存区为空后,再次读取会返回
默认值和false
写数据
- 往
nil通道写数据-->阻塞 - 往关闭的通道写数据-->
panic异常 - 往无缓存通道写数据,在数据被读取前会陷入阻塞
- 往缓存区通道写入数据,缓存区写满后会陷入阻塞