Go 通道(chan)关闭和后续读取操作

本文详细介绍了Go语言中通道的使用技巧,包括通道关闭时机、读取关闭通道的行为差异(无缓存与有缓存的区别)、以及如何正确使用range遍历通道等内容。

1、通道关闭时间:
一般紧跟在往通道输入最后一个数据之后。

    jobs := make(chan int, 5)
    for i := 1; i < 4; i++ {
        jobs <- i
        fmt.Println("sent job", i)
        //      if i == 3 {
        //          close(jobs)
        //      }
    }
    close(jobs)

2、读取关闭的无缓存通道:
读取关闭后的无缓存通道,不管通道中是否有数据,返回值都为0和false。

    done := make(chan int)
    go func() {
        done <- 1
    }()
    close(done)
    for i := 1; i <= 3; i++ {
        t, ok := <-done
        fmt.Println(i, ":", t, ok)
    }

运行结果:
1:0 false
2:0 false
3:0 false

3、读取关闭的有缓存通道:
读取关闭后的有缓存通道,将缓存数据读取完后,再读取返回值为0和false。

    done := make(chan int 1)
    go func() {
        done <- 1
    }()
    close(done)
    for i := 1; i <= 3; i++ {
        t, ok := <-done
        fmt.Println(i, ":", t, ok)
    }

运行结果:
1:1 true
2:0 false
3:0 false

4、range遍历通道:
通道写完后,必须关闭通道,否则range遍历会出现死锁。

### 通道Channel)的基本概念 Go语言中的通道是一种用于在goroutine之间进行通信同步的机制。它提供了一种安全的方式,使得不同的goroutine可以共享数据而无需使用锁[^1]。 ### 创建通道Go语言中,通道是引用类型,需要使用`make`函数来创建。基本语法如下: ```go channel := make(chan T) ``` 其中`T`表示通道传递的数据类型。例如,创建一个整型类型的通道可以这样写: ```go ch1 := make(chan int) ``` 如果需要创建一个可以存放任意类型数据的通道,则可以使用空接口类型: ```go ch2 := make(chan interface{}) ``` ### 发送与接收数据 向通道发送数据使用 `<-` 操作符,从通道接收数据同样使用该操作符。下面是一个简单的例子: ```go package main import "fmt" func main() { ch := make(chan string) go func() { ch <- "Hello from goroutine" }() msg := <-ch fmt.Println(msg) } ``` 在这个例子中,子协程向通道`ch`发送了一个字符串,主协程随后从这个通道接收到了这条消息并打印出来[^4]。 ### 带缓冲的通道 默认情况下,通道是没有缓冲的,这意味着发送方会阻塞直到有接收方准备好。可以通过指定第二个参数给`make`函数来创建带缓冲的通道: ```go bufferedCh := make(chan int, 3) // 创建一个带有3个元素缓冲区的通道 ``` 当缓冲区满时,发送操作将被阻塞;当缓冲区为空时,接收操作将被阻塞[^2]。 ### 单向通道 有时我们希望限制通道的方向,即只允许发送或只允许接收。这可以通过类型转换实现。比如,在一个函数内部,我们可以将双向通道转换为仅能发送或者仅能接收的通道: ```go func sendData(sendCh chan<- int) { sendCh <- 42 } func main() { ch := make(chan int) go sendData(ch) fmt.Println(<-ch) } ``` 这里`sendData`函数接受一个只能发送的通道作为参数,确保了在此函数内只能执行发送操作[^5]。 ### 多路复用 Go语言支持通过`select`语句处理多个通道上的通信。这对于构建复杂的并发结构非常有用: ```go package main import ( "fmt" "time" ) func main() { c1 := make(chan string) c2 := make(chan string) go func() { time.Sleep(time.Second * 1) c1 <- "one" }() go func() { time.Sleep(time.Second * 2) c2 <- "two" }() for i := 0; i < 2; i++ { select { case msg1 := <-c1: fmt.Println("received", msg1) case msg2 := <-c2: fmt.Println("received", msg2) } } } ``` 这段代码演示了如何同时监听两个不同时间延迟后发送的消息,并按接收到的顺序处理它们[^2]。 ### 关闭通道 当不再有数据要发送到通道时,应该关闭它以通知接收者。使用`close`函数来完成这项工作: ```go ch := make(chan int) // ... some code ... close(ch) ``` 一旦通道关闭,任何后续的发送操作都会导致panic。但是可以从已关闭通道接收数据,直到所有已发送的数据都被读取完毕为止。利用这一点,我们可以方便地控制循环结束条件: ```go for value := range channel { // 处理value... } ``` 当通道关闭且没有剩余值可读时,上述循环自动终止[^5]。
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值