channel负责goroutine之间通信,channel的本质是一个队列,先进先出。
创建channel
有两种类型的channel
- 创建有缓冲channel:
make(chan int, 10) - 创建无缓冲channel:
make(chan int)
从管道中读取,或者向管道写入数据,使用运算符:<-
- ch <- 10
- num := <- ch
无缓冲通道
无缓冲通道没有内部存储空间,数据的发送和接收必须同步完成,即写端与读端同时存在,写入一个数据,则能读出一个数据。
- 发送方(
send):必须等到接收方(receive)准备好接收数据时才能继续。 - 接收方(
receive):必须等到发送方发送数据时才能接收数据。 强调同步性
有缓冲通道
带缓冲通道在缓冲区未满时支持非阻塞写入,但一旦缓冲区写满,它的行为会变得类似无缓冲通道,也会导致写操作阻塞,直到缓冲区有空间可以接收新数据。相当于具备了异步的能力。
通道其他操作
关闭 close(ch)
不能再次发送数据,仍然可以读取数据 从通道中接收数据时,可以利用多返回值(ok)判断通道是否已经关闭 func main() {
func main() {
ch := make(chan int, 10)
go func(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}(ch)
for {
if num, ok := <-ch; ok == true {
fmt.Println("读到数据:", num)
} else {
break
}
}
}
单向通道
可以将 channel 声明为只读或只写。
var sendOnly chan<- int = make(chan int)
sendOnly <- 42 // 只能发送
var receiveOnly <-chan int = make(chan int)
val := <-receiveOnly // 只能接收
应用场景
限制并发数量
例如限制为5,timeMore通过 <- 操作向通道中写入任务。如果通道已满,写入操作会阻塞,等待通道腾出空间。而main中创建了一个带缓冲的字符串通道 ch,缓冲区大小为 5,即最多允许 5 个任务同时注册到通道。
func timeMore(ch chan string) {
// 执行前先注册,写不进去就会阻塞
ch <- "任务"
fmt.Println("模拟耗时操作")
time.Sleep(time.Second) // 模拟耗时操作
// 任务执行完毕,则管道中销毁一个任务
<-ch
}
func main() {
ch := make(chan string, 5)
// 开启100个协程
for i := 0; i < 100; i++ {
go timeMore(ch)
}
for {
time.Sleep(time.Second * 5)
}
}
生产者消费者
设置一个带缓冲区的通道
- ch := make(chan int, 5)
- 生产者:向ch中写数据
- 消费者:从ch中读数据
定时器
通道是定时器 time.Timer 的核心机制。
结构体定义:
type Timer struct {
C <-chan Time // 一个只读的时间通道,用于接收定时器触发的时间值
r runtimeTimer // 运行时定时器对象,隐藏实现细节
}
- 当调用
time.NewTimer创建定时器时,会初始化一个C通道。 - 如果尝试读取通道
C,在定时时间到达前通道中没有数据,因此读取操作会阻塞。 - 一旦定时时间到达,系统会自动向通道
C中写入当前时间值,从而解除阻塞。