如何判断一个缓冲通道是否已满?

huangapple go评论180阅读模式
英文:

How to know a buffered channel is full

问题

如何判断缓冲通道是否已满?当缓冲通道已满时,我不想被阻塞,而是选择丢弃发送到缓冲通道的项目。

英文:

How to know a buffered channel is full? I don't know to be blocked when the buffered channel is full, instead I choose to drop the item sent to the buffered channel.

答案1

得分: 188

你可以使用带有默认情况的select语句。如果无法执行任何一个case,比如向一个满的通道发送数据,该语句将执行默认情况:

package main

import "fmt"

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

    // 填满通道
    ch <- 1

    select {
    case ch <- 2: // 除非通道已满,否则将2放入通道
    default:
        fmt.Println("通道已满。丢弃值")
    }
}

输出:

通道已满。丢弃值

Playground: http://play.golang.org/p/1QOLbj2Kz2

无需发送进行检查

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

if len(ch) == cap(ch) {
    // 通道已满,但现在可能不满
} else {
    // 通道未满,但现在可能满了
}

请注意,比较的结果可能在进入if块之前就已经失效了。

英文:

You can use the select statement with a default. In case it is not possible to do any of the cases, like sending to a full channel, the statement will do the default:

package main

import &quot;fmt&quot;

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

	// Fill it up
	ch &lt;- 1

	select {
	case ch &lt;- 2: // Put 2 in the channel unless it is full
	default:
		fmt.Println(&quot;Channel full. Discarding value&quot;)
	}
}

Output:

>Channel full. Discarding value

Playground: http://play.golang.org/p/1QOLbj2Kz2

Check without sending

It is also possible to check the number of elements queued in a channel by using len(ch), as stated in the Go specifications.
This in combination with cap allows us to check if a channel is full without sending any data.

if len(ch) == cap(ch) {
    // Channel was full, but might not be by now
} else {
    // Channel wasn&#39;t full, but might be by now
}

Note that the result of the comparison may be invalid by the time you enter the if block

答案2

得分: 16

> 而我选择放弃发送到缓冲通道的项目。

这被称为"溢出通道",你可以在eapache/channels/overflowing_channel.go中找到ANisus的答案实现:

for elem := range ch.input {
    // 如果无法立即写入,就放弃并继续下一个
    select {
    case ch.output <- elem:
    default:
    }
}
close(ch.output)

但是该项目eapache/channels还实现了其他策略:

  • OverflowingChannel以一种不阻塞写入者的方式实现了Channel接口。
    具体来说,如果在OverflowingChannel的缓冲区已满时(或者在无缓冲的情况下,接收者未准备好时)写入一个值,那么该值将被简单地丢弃。

> 要实现相反的行为(丢弃最旧的元素而不是最新的元素),请参见RingChannel

英文:

> instead I choose to drop the item sent to the buffered channel.

That is called "overflowing channel", and you find ANisus's answer implemented in eapache/channels/overflowing_channel.go:

for elem := range ch.input {
    // if we can&#39;t write it immediately, drop it and move on
    select {
    case ch.output &lt;- elem:
    default:
    }
}
close(ch.output)

But that project eapache/channels implements other strategies as well:

  • OverflowingChannel implements the Channel interface in a way that never blocks the writer.
    Specifically, if a value is written to an OverflowingChannel when its buffer is full
    (or, in an unbuffered case, when the recipient is not ready) then that value is simply discarded.

> For the opposite behaviour (discarding the oldest element, not the newest) see RingChannel.

答案3

得分: 5

我发现的另一个有用的例子是这个巧妙的实现环形缓冲区

引用来源:

这个想法很简单:通过一个Goroutine将两个缓冲通道连接起来,将消息从输入通道转发到输出通道。每当无法将新消息放入输出通道时,从输出通道中取出一条消息(即缓冲区中最旧的消息),丢弃它,并将新消息放入刚刚释放的输出通道中。

还可以查看这个C版本...

英文:

Another useful example I stumbled upon was this nifty implementation of Ring Buffer.

The quote from the source:

> The idea is simple: Connect two buffered channels through one
> Goroutine that forwards messages from the incoming channel to the
> outgoing channel. Whenever a new message can not be placed on on the
> outgoing channel, take one message out of the outgoing channel (that
> is the oldest message in the buffer), drop it, and place the new
> message in the newly freed up outgoing channel.

Check out this C version as well...

答案4

得分: 2

我使用这段代码来在通道满时移除一个元素。对于只有一个发送例程的通道,确保发送到 ch 后仍然有效。

// 如果通道满了,则从通道中移除一个元素
if len(ch) == cap(ch) {
    // 通道满了,但现在可能不满了
    select {
    case _ := <-ch:
        // 丢弃一个元素
    default:
        // 可能它已经是空的了
    }
}

// 现在我们可以发送到通道了

这段代码的作用是,如果通道已满,则从通道中移除一个元素,以确保后续可以成功发送到该通道。

英文:

I use this code to remove one item if the channel was full. For channels with only one sending go routine it's enough to ensure that sending to ch will work afterwards.

// Remove one item from chan if full
if len(ch) == cap(ch) {
	// Channel was full, but might not be by now
	select {
	case _ := &lt;-ch:
	// Discard one item
	default:
		// Maybe it was empty already
	}
}

// Now we can send to channel

huangapple
  • 本文由 发表于 2014年9月4日 12:16:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/25657207.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定