如何测试通道是不是关闭并仅在未关闭时发送给它

Posted

技术标签:

【中文标题】如何测试通道是不是关闭并仅在未关闭时发送给它【英文标题】:How to test if a channel is close and only send to it when it's not closed如何测试通道是否关闭并仅在未关闭时发送给它 【发布时间】:2017-01-05 21:31:21 【问题描述】:

在 Go 中,如果通道 channel 已关闭,我仍然可以使用以下语法从中读取,并且可以测试 ok 以查看它是否已关闭。

value, ok := <- channel
if !ok 
    // channel was closed and drained

但是,如果我不知道某个通道是否已关闭而盲目地写入它,我可能会收到错误消息。我想知道是否有任何方法可以测试通道并仅在通道未关闭时写入。我问这个问题是因为有时我不知道一个通​​道是否在 goroutine 中关闭。

【问题讨论】:

如果你不知道一个通​​道是否关闭而盲目地写入它,那么你的程序设计得很糟糕。重新设计,这样关闭后就无法写入了。 是的@ain,我完全同意你的看法!我只是想知道,从技术上讲,是否有办法做到这一点。 相关/可能重复:Closing channel of unknown length 这能回答你的问题吗? How to check a channel is closed or not without reading it? 【参考方案1】:

你不能。这里的经验法则是只有作家才能关闭频道,这样你就知道你不应该再向那个频道写信了。

一些简单的代码如下所示:

for i := 0; i < 100; i++ 
    value := calculateSomeValue()
    channel <- value


close(channel) //indicate that we will no more send values

【讨论】:

如果多个 goroutine 写入同一个通道怎么办?如果一个 goroutine 完成了它的工作,它不能简单地关闭通道。其他 goroutine 可能仍需要写入通道。 @OgrishMan:那么当所有 goroutines 完成时,你有一个外部观察者关闭通道,或者根本不关闭它。 @OgrishMan 在这个答案中使用sync.WaitGroup查看解决方案:Closing channel of unknown length。【参考方案2】:

如果很少有 goroutin 写入通道,您也可以 nil 它而不是 close 并使用 select 进行读写。像这样的

ch := make(chan int, 1)
var value int
ch <- 5
select 
case value = <-ch:
    fmt.Println("value", value)
default:
    fmt.Println("oops")

ch = nil
select 
case ch <- 5:
default:
    fmt.Println("don't panic")

select 
case value = <-ch:
    fmt.Println("value", value)
default:
    fmt.Println("oops")

试试看https://play.golang.org/p/sp8jk961TB

【讨论】:

默认是表示当前没有数据还是通道关闭了继续阅读? 这里有一些代码可以回答你的问题:` close(ch) select case value, ok := 它将落入default,就像switch 声明一样。我添加了以下代码来测试通道是否关闭。如果通道关闭并且值将为 0,则 ok var 将为 false,这是 int 的默认 nil 值。这里有一些代码可以回答你的问题:` close(ch) select case value, ok := 如果您从多个 go-routine 写入通道,则会出现竞争条件。您需要使用锁或使用原子指针来保护对通道的访问。 即使使用select,尝试写入已关闭的频道时也会出现恐慌,请参阅此代码play.golang.org/p/Q4m4_CJl-sJ【参考方案3】:

你不能。您可以将消息发送到通道的工作和另一个从通道读取的 go 例程分开。

您应该添加一个 donechannel 以在阅读器完成时发出信号。

示例。

package main

import (
    "fmt"
)

func main() 

  mychan := make(chan int)
  donechannel := make(chan struct)
  go pushchannel(mychan)
  go drainchan(mychan, donechannel)
  _, ok := <-donechannel; if !ok 
     fmt.Println("can not read from donechannel this means donechannel is closed, means we are done :)")
  
  fmt.Println("Done")


func pushchannel(ch chan int) 
 fmt.Println("pushing to chan")
 for i:=0; i<=10; i++ 
     fmt.Printf("pushed %v\n",i)
     ch<-i
 
 close(ch)


func drainchan(ch chan int, donechannel chan struct) 
  fmt.Println("draining")
  for 
    res, ok := <- ch
    if !ok 
       fmt.Println("result channel is closed, we can signal the donechannel now.")
        close(donechannel)
       break 
     else 
      fmt.Printf("got result %v\n", res)
    
  

https://play.golang.org/p/BMyMkrqWF7s

【讨论】:

以上是关于如何测试通道是不是关闭并仅在未关闭时发送给它的主要内容,如果未能解决你的问题,请参考以下文章

仅在应用程序/小部件关闭时提交对 Firestore 的更改

slideToggle jQuery 在加载时关闭

如何关闭多个 goroutine 正在发送的通道?

Winsock 仅在程序关闭时发送数据

Golang 编译时发送到零通道 [关闭]

Spring AMQP RPC使用者如何确定发布者通道是不是已关闭