【问题标题】: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 中关闭。

【问题讨论】:

标签: go channel


【解决方案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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-17
    • 1970-01-01
    • 2018-04-10
    • 2018-12-01
    • 2017-01-08
    • 2021-11-17
    • 2021-03-10
    • 1970-01-01
    相关资源
    最近更新 更多