了解chan
在 Go(Golang)中,chan
是用来实现 goroutine 之间通信的管道(Channel)。它允许你在线程之间安全地传递数据。
创建chan
go
chOne := make(chan int) // 创建一个整型的无缓冲 Channel
chTwo := make(chan string, 2) // 创建一个字符串的缓冲 Channel,容量为 2
发送和接收数据
无缓冲的 Channel 在发送或接收时会阻塞,直到另一端准备好。
go
ch := make(chan int)
// 发送数据到 Channel
go func() {
fmt.Println("1秒后向chan发送数据")
time.Sleep(1 * time.Second)
ch <- 1
}()
// 从 Channel 接收数据
fmt.Println("main等待接收数据")
data := <-ch
fmt.Println(data) // 输出: 1
fmt.Println("结束进程")
运行结果 带缓冲的chan
- 缓冲 Channel 可以存储有限数量的数据,而不需要立即被消费。
- 如果缓冲已满,发送操作会阻塞,直到缓冲中有空位。
go
ch := make(chan int, 2)
ch <- 1
fmt.Println("这里不会阻塞")
ch <- 2
fmt.Println(<-ch) // 输出: 1
fmt.Println(<-ch) // 输出: 2
运行结果
使用 select
语句
select 用于同时监听多个 Channel 的读写操作。
go
ch1 := make(chan int)
ch2 := make(chan string)
go func() {
ch1 <- 1
}()
go func() {
ch2 <- "Hello"
}()
select {
case msg := <-ch1:
fmt.Println("接收到数据 ch1:", msg)
case msg := <-ch2:
fmt.Println("接收到数据 ch2:", msg)
default:
fmt.Println("无数据")
}
- select可以同时监听多个ch,上面代码中,那个先接收到数据就执行那个case
- 如果有default,ch1,ch2为空的情况下,执行default 不堵塞
- 如果没有default,ch1,ch2为空的情况下,select 会堵塞,直到ch1或ch2任何一个有值
- 直接使用select{} 不设置任何case,也会导致当前方法堵塞
运行结果
ps : 这个结果是随机的,取决那个case先接收到数据,如果两个go协程没有先于select执行,也就是ch1,ch2还没有值,就执行default 无数据
关闭 Channel
使用 close()
关闭 Channel,表示没有更多数据会发送到这个 Channel。
- 关闭 Channel 后再发送数据会导致 panic。
- 从已关闭的 Channel 接收数据会返回零值。
- 可以通过接收第二个值来检测 Channel 是否已关闭。
go
ch := make(chan int)
go func() {
for i := 0; i < 3; i++ {
time.Sleep(1 * time.Second)
ch <- i
}
close(ch) // 关闭 Channel
}()
for val := range ch { // 使用 range 读取 Channel,直到关闭
fmt.Println(val)
}
// chan的第二个值可以判断chan是否被关闭,类似map取值
chHas := make(chan int)
close(chHas)
val, ok := <-chHas
fmt.Println(val, ok) // 输出: 0 false
fmt.Println("程序结束")
运行结果
使用场景
- 数据流传递:goroutine 之间传递数据。
- 任务协调:通过 Channel 来同步任务。
- 信号通知:通过关闭 Channel 实现广播信号。
通过 chan
和 select
,可以轻松实现高并发任务的同步和数据通信,是 Go 并发编程的核心之一。