通道技巧,关闭通道后还能继续用

简介: 通道技巧,关闭通道后还能继续用

概述

在 Go 语言中,通道(Channel)是一种强大的并发机制,但很多开发者在关闭通道后是否还能使用存在疑惑。

本文将探讨在关闭通道后继续利用通道的技巧,解释其原理,并通过实例代码演示在实际应用中的巧妙用法。


 

1. 通道关闭的基本原理

在 Go 语言中,通过 close 函数可以关闭一个通道。

关闭通道后,将不能再向通道发送数据,但仍然可以从通道接收数据。

这是因为关闭通道后,接收者仍然可以读取通道中已有的数据,直到通道中的数据被全部读取完毕。


 

2. 关闭通道后的读取


package main
import (  "fmt"  "time")
func main() {  ch := make(chan int, 3)
  go func() {    defer close(ch)    for i := 1; i <= 3; i++ {      ch <- i      time.Sleep(time.Second)    }  }()
  // 读取通道中的数据  for {    select {    case num, ok := <-ch:      if !ok {        fmt.Println("通道已关闭")        return      }      fmt.Println("接收到数据:", num)    default:      // 可以进行其他操作      time.Sleep(500 * time.Millisecond)    }  }}

在示例中,创建了一个带缓冲的通道,并在一个 goroutine 中向通道发送数据,每发送一次就休眠 1 秒。

在主 goroutine 中,通过 select 语句监听通道的数据,并在通道关闭后打印提示信息。


 

3. 关闭通道后的写入


package main
import (  "fmt"  "time")
func main() {  ch := make(chan int, 3)    go func() {    defer close(ch)    for i := 1; i <= 3; i++ {      ch <- i      time.Sleep(time.Second)    }  }()
  // 写入数据到已关闭的通道  for i := 4; i <= 6; i++ {    select {    case ch <- i:      fmt.Println("写入数据:", i)    default:      fmt.Println("通道已关闭,写入失败")    }  }}

在上述示例中,同样创建了一个带缓冲的通道,并在一个 goroutine 中向通道发送数据。

在主 goroutine 中,尝试向已关闭的通道写入数据,并通过 select 语句判断通道的状态,实现对写入的合理控制。


 

4. 应用场景:扇出与扇入

4.1 扇出

扇出是指将一个通道的数据分发给多个 goroutine 进行处理。

关闭通道后,接收者可以继续处理通道中的数据,实现数据的扇出。


package main
import (  "fmt"  "sync"  "time")
func main() {  ch := make(chan int, 5)
  go func() {    defer close(ch)    for i := 1; i <= 5; i++ {      ch <- i      time.Sleep(500 * time.Millisecond)    }  }()
  var wg sync.WaitGroup  for i := 0; i < 3; i++ {    wg.Add(1)    go func(id int) {      defer wg.Done()      for {        select {        case num, ok := <-ch:          if !ok {            fmt.Printf("协程%d:通道已关闭\n", id)            return          }          fmt.Printf("协程%d:接收到数据:%d\n", id, num)        default:          // 可以进行其他操作          time.Sleep(300 * time.Millisecond)        }      }    }(i)  }  wg.Wait()}

在上面示例中,创建了一个带缓冲的通道,并在一个 goroutine 中向通道发送数据。

然后启动了三个协程,每个协程通过 select 语句监听通道的数据,实现了数据的扇出。

4.2 扇入

扇入是指将多个通道的数据汇总到一个通道中。

关闭通道后,接收者可以继续从已关闭的多个通道中读取数据,实现数据的扇入。


package main
import (  "fmt"  "sync"  "time")
func main() {  ch1 := make(chan int, 3)  ch2 := make(chan int, 3)  outCh := make(chan int, 6)
  go func() {    defer close(ch1)    for i := 1; i <= 3; i++ {      ch1 <- i      time.Sleep(500 * time.Millisecond)    }  }()
  go func() {    defer close(ch2)    for i := 4; i <= 6; i++ {      ch2 <- i      time.Sleep(500 * time.Millisecond)    }  }()
  var wg sync.WaitGroup  wg.Add(2)
  go func() {    defer wg.Done()    for {      select {      case num, ok := <-ch1:        if !ok {          fmt.Println("通道ch1已关闭")          return        }        outCh <- num      default:        // 可以进行其他操作        time.Sleep(300 * time.Millisecond)      }    }  }()
  go func() {    defer wg.Done()    for {      select {      case num, ok := <-ch2:        if !ok {          fmt.Println("通道ch2已关闭")          return        }        outCh <- num      default:        // 可以进行其他操作        time.Sleep(300 * time.Millisecond)      }    }  }()
  go func() {    wg.Wait()    close(outCh)  }()
  for num := range outCh {    fmt.Printf("接收到合并通道的数据:%d\n", num)  }}

在上面示例中,创建了两个带缓冲的通道 ch1ch2 ,并在两个 goroutine 中向这两个通道发送数据。

然后启动了两个协程,每个协程通过 select 语句监听各自通道的数据,并将数据写入一个输出通道 outCh 中,最后在主 goroutine 中读取outCh 中的数据。


 

5. 总结

通过本文的讲解和示例代码,了解了在 Go 语言中关闭通道后继续使用通道的技巧。

这一特性可以在一些实际应用场景中发挥重要作用,如扇出与扇入。

关闭通道后继续使用通道看起来有些反直觉,但是这种设计提供了一定的灵活性。

在编码时,还是要考虑通道的同步逻辑,谨慎使用这种特性,避免引起数据竞争等问题。

目录
相关文章
|
3月前
|
存储 消息中间件 缓存
|
4月前
|
存储 Go
GO通道:无缓冲通道与缓冲通道
GO通道:无缓冲通道与缓冲通道
39 0
|
6月前
|
计算机视觉 索引
图像通道操作
【5月更文挑战第8天】图像通道操作。
48 4
|
6月前
|
JavaScript 前端开发
在页面中监听多个广播通道
使用 JavaScript 的 `BroadcastChannel` 可以监听多个广播通道。示例展示了如何创建并处理两个通道&quot;channel1&quot;和&quot;channel2&quot;的事件。每个通道都有独立的`onmessage`事件处理程序,接收到消息时会在控制台打印。可以按需创建多个通道,并在不再需要时调用`channel.close()`关闭以释放资源。
|
缓存 Linux
流的打开和关闭
流的打开和关闭
146 0
|
算法 物联网
设备通过mqtt通道的动态免预注册
一型一密认证方式下,同一产品下所有设备可以烧录相同的设备标志信息,即所有设备包含相同的产品证书(ProductKey和ProductSecret)。设备发送激活请求时,物联网平台会进行身份确认,认证通过后,下发设备接入所需信息。
452 0
|
芯片
DRV8848通道输出不正常原因
DRV8848通道输出不正常原因
|
缓存
多线程之间的通信~~~管道通道
多线程之间的通信~~~管道通道
224 0
|
Android开发 数据格式
【Android 高性能音频】AAudio 音频流 样本缓冲 相关配置 ( 通道数 | 样本格式 | 帧缓冲 | 采样率 | 每帧样本数 == 通道数 )(一)
【Android 高性能音频】AAudio 音频流 样本缓冲 相关配置 ( 通道数 | 样本格式 | 帧缓冲 | 采样率 | 每帧样本数 == 通道数 )(一)
222 0