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

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

概述

在 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 语言中关闭通道后继续使用通道的技巧。

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

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

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

目录
相关文章
|
4月前
|
Python
【金融量化】通道突破策略之布林带策略(Bollinger Band )、肯特纳通道策略(Keltner Channel)、唐奇安通道策略(Donchian)原理简介
本文介绍了三种金融量化分析中的通道突破策略:布林带策略(Bollinger Band)、肯特纳通道策略(Keltner Channel)和唐奇安通道策略(Donchian Channel),并提供了每种策略的原理和Python实现代码。
225 2
|
4月前
|
存储 消息中间件 缓存
|
5月前
|
存储 Go
GO通道:无缓冲通道与缓冲通道
GO通道:无缓冲通道与缓冲通道
43 0
|
7月前
|
计算机视觉 索引
图像通道操作
【5月更文挑战第8天】图像通道操作。
51 4
|
7月前
|
JavaScript 前端开发
在页面中监听多个广播通道
使用 JavaScript 的 `BroadcastChannel` 可以监听多个广播通道。示例展示了如何创建并处理两个通道&quot;channel1&quot;和&quot;channel2&quot;的事件。每个通道都有独立的`onmessage`事件处理程序,接收到消息时会在控制台打印。可以按需创建多个通道,并在不再需要时调用`channel.close()`关闭以释放资源。
|
存储 算法 安全
设备通过mqtt通道的动态预注册
在物联网平台为产品开启动态注册功能后,直连设备可使用一型一密安全认证方式完成动态注册,通过MQTT通信协议连接物联网平台。设备先基于TLS建立与物联网平台的连接,获取MQTT连接所需的设备密钥,再断开连接,然后重新建立MQTT连接进行通信。
549 1
|
算法 物联网
设备通过mqtt通道的动态免预注册
一型一密认证方式下,同一产品下所有设备可以烧录相同的设备标志信息,即所有设备包含相同的产品证书(ProductKey和ProductSecret)。设备发送激活请求时,物联网平台会进行身份确认,认证通过后,下发设备接入所需信息。
474 0
|
安全 容灾 测试技术
一文带你了解高速通道三大使用场景
高速通道适用于本地IDC与云上专有网络建立可靠、安全和高速的私网通信场景。高速通道提供多种上云服务,您可以根据业务场景进行选择,轻松构建跨架构的融合网络。
1092 0
|
网络安全 数据中心 网络架构
操作高速通道 配置健康检查,只需四步!
阿里云每两秒从健康检查IP地址向本地数据中心中的客户侧互联IP发送一个ping报文,如果某条物理专线上连续8个ping报文都无法得到回复,则将流量切换至另一条链路。
879 0