Appearance
通道
- 通道(Channel)是用于 Goroutine 之间的数据传递。
定义
- 格式:
var ch chan int = make(chan int, length)
go
// 方式1
var ch1 chan int = make(chan int, 10)
// 方式2
ch2 := make(chan int, 10)发送数据
- 发送数据到通道:使用
<-运算符将数据发送到通道。
go
// 定义一个通道
ch := make(chan int, 10)
// 发送数据到通道
ch <- 10
ch <- 20接收数据
- 从通道接收数据:使用
<-运算符从通道接收数据。
go
// 定义一个通道
ch := make(chan int, 10)
// 发送数据到通道
ch <- 10
ch <- 20
// 从通道接收数据
x := <-ch关闭通道
- 关闭通道:使用
close()函数关闭通道。 - 关闭通道后,不能再发送数据到通道。
- 关闭通道后,可以继续从通道接收数据,直到通道为空,通道取完了,之后灾区取出的结果0。
go
// 关闭通道
close(ch)无缓冲管道(同步管道)
- 无缓冲管道:在创建通道时不指定缓冲区大小,通道的发送和接收操作是同步的。
go
// 定义一个通道
ch := make(chan int)
// 发送数据到通道, 必须在另一协程操作, 否则会发生死锁
go func() {
ch <- 10
ch <- 20
}()
// 从通道接收数据
x := <-ch
y := <-ch
fmt.Println(x, y)有缓冲管道(异步管道)
go
// 定义一个通道
ch := make(chan int,2)
// 可以同步操作,但是不能超过缓冲区大小
ch <- 10
ch <- 20
// 从通道接收数据
x := <-ch
y := <-ch
fmt.Println(x, y)死锁分析
案例1
go
ch := make(chan int)
ch <- 10- 死锁原因:在无缓冲通道中,发送操作和接收操作是同步的,上述代码只有发送数据,没有接收数据,所以会发生死锁。
- 解决方法:在发送数据之前,先启动一个协程接收数据。
案例2
go
ch := make(chan int)
ch <- 10
<-ch- 死锁原因:主 goroutine 执行 ch <- 10 时,由于是无缓冲通道,发送操作会阻塞,直到有其他 goroutine 执行对应的接收操作。但此时后续的 <-ch 代码根本没有机会执行(因为主 goroutine 已经在 ch <- 10 处阻塞了),导致没有接收方来解除阻塞,最终触发死锁。
- 解决方法:在发送数据之前,先启动一个协程接收数据。
go
ch := make(chan int)
go func() {
x := <-ch
fmt.Println(x)
}()
ch <- 10案例3
go
ch := make(chan int, 2)
ch <- 10
ch <- 20
ch <- 30- 死锁原因:缓存区满了,发送操作会死锁。
- 解决方法:及时消耗管道数据,避免缓存区满。
单向管道
- 管道可以被限制为只发送或只接收(通常用于函数参数,明确数据流向)
go
// 只发送管道:只能发送数据,不能接收
type SendChan chan<- int
// 只接收管道:只能接收数据,不能发送
type RecvChan <-chan int
// 函数参数为只发送管道
func sendData(ch chan<- int) {
ch <- 100 // 允许发送
// <-ch // 错误:只发送管道不能接收
}
// 函数参数为只接收管道
func recvData(ch <-chan int) {
fmt.Println(<-ch) // 允许接收
// ch <- 200 // 错误:只接收管道不能发送
}
// 定义一个 只发送通道
ch := make(chan int)
go sendData(ch)
recvData(ch)管道控制
select语句可以同时监听多个通道,根据通道是否有数据可读或可写来执行不同的操作。
go
ch1 := make(chan int)
go func() {
time.Sleep(2 * time.Second)
ch1 <- 100
}()
ch2 := make(chan int, 2)
go func() {
for i := 1; i < 10; i++ {
ch2 <- i * 10
time.Sleep(1 * time.Second)
}
close(ch2)
}()
for {
// 循环监听通道,直到通道关闭
select {
case num1 := <-ch1:
fmt.Println("收到数据ch1:", num1)
case num2 := <-ch2:
fmt.Println("收到数据ch2:", num2)
default:
fmt.Println("没有数据")
}
}