Golang - 如何知道缓冲通道已满

Gar*_*iga 37 channel go

如何知道缓冲通道已满?我不知道在缓冲通道已满时被阻止,而是我选择丢弃发送到缓冲通道的项目.

ANi*_*sus 95

您可以使用默认select语句.如果无法执行任何情况,例如发送到完整通道,则语句将执行以下操作:

package main

import "fmt"

func main() {
    ch := make(chan int, 1)

    // Fill it up
    ch <- 1

    select {
    case ch <- 2: // Put 2 in the channel unless it is full
    default:
        fmt.Println("Channel full. Discarding value")
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

频道已满.抛弃价值

游乐场: http ://play.golang.org/p/1QOLbj2Kz2

不发送检查

len(ch)Go规范中所述,还可以通过使用来检查在通道中排队的元素的数量.结合使用cap,我们可以在不发送任何数据的情况下检查通道是否已满.

if len(ch) == cap(ch) {
    // Channel was full, but might not be by now
} else {
    // Channel wasn't full, but might be by now
}
Run Code Online (Sandbox Code Playgroud)

请注意,在您输入if块时,比较结果可能无效

  • @Tom你可以实际测试`if len(ch)== cap(ch){...}`其中`len(ch)`是通道中的项目数,'cap(ch)`是容量.但是,当您输入if块时,它可能无效. (14认同)
  • 但是,如果只想检查缓冲区是否已满,即使没有写入缓冲区也不会写入该怎么办?有没有办法做到这一点? (2认同)
  • 如果通道已满并且您的选择达到“默认”,则之后它可能不会满;根据您的使用情况,``len(ch) == cap(ch)``` 也同样有用 (2认同)

Von*_*onC 12

相反,我选择删除发送到缓冲通道的项目.

这被称为"溢出通道",你会发现ANisus的答案在eapache/channels/overflowing_channel.go:

for elem := range ch.input {
    // if we can't write it immediately, drop it and move on
    select {
    case ch.output <- elem:
    default:
    }
}
close(ch.output)
Run Code Online (Sandbox Code Playgroud)

但该项目的eapache/channels也实现了其他策略:

  • OverflowingChannelChannel以永不阻止编写器的方式实现接口.
    具体来说,如果将值写入OverflowingChannel其缓冲区已满时
    (或者,在无缓冲的情况下,当收件人未准备好时),则简单地丢弃该值.

对于相反的行为(丢弃最旧的元素,而不是最新的元素)请参阅RingChannel.

  • 一个很好的答案.丢失最旧或最新项目的溢出渠道有时可能是您工具箱中的重要工具.考虑一个goroutines环:通常,任何循环(aka*cycle*)都会带来死锁风险.将任何一个通道更改为溢出通道可以解决此问题.丢失一些事件并不重要,如果它们可以变得陈旧,可以很容易地被替换.当然还有其他方法来解决同样的死锁问题. (2认同)

Ost*_*ati 5

另一个有用的例子,我偶然发现了这个漂亮的实现环形缓冲区

引自来源:

这个想法很简单:通过一个 Goroutine 连接两个缓冲通道,该 Goroutine 将消息从传入通道转发到传出通道。每当无法将新消息放置在传出通道上时,从传出通道中取出一条消息(即缓冲区中最旧的消息),将其丢弃,然后将新消息放置在新释放的传出通道中。

也看看这个 C 版本......