如何实现对无缓冲通道的非阻塞写入?

How to implement non-blocking write to an unbuffered channel?

来自Effective Go

Receivers always block until there is data to receive. If the channel is unbuffered, the sender blocks until the receiver has received the value.

但是 signal.Notify 将信号中继到无缓冲通道而不会阻塞。这是如何工作的,是否可以与其他 chan<- 类型一起使用?

当它说 os.Notify 不会阻止时,它的意思是如果它被阻止,消息将被丢弃。因此,虽然它不会阻塞是真的,但如果不能立即接收信号,它会中继信号是不正确的。这是通过简单的 select:

完成的
select {
    case channel <- message:
        // message sent
    default:
        // message dropped
}

这就是 Notify 的文档明确指出您应该使用缓冲通道的原因。另请注意,缓冲通道也可以阻塞,而不仅仅是无缓冲通道;缓冲通道仅在缓冲区已满时才阻塞。

select 包含在 the tour and the spec.

您始终可以避免阻塞,同时(可能)仍然可以通过使用另一个 goroutine 来保证交付:

go func() { channel <- message }()

当然,这只是使用 goroutine 调度程序作为您频道的替代缓冲区,这可能是明智的,也可能不是明智的。

Which is why the documentation for Notify explicitly states that you should use a buffered channel

使用 Go 1.17, the tool vet 也会使这一点更清楚:

New warning for calling signal.Notify on unbuffered channels

The vet tool now warns about calls to signal.Notify with incoming signals being sent to an unbuffered channel.

Using an unbuffered channel risks missing signals sent on them as signal.Notify does not block when sending to a channel.

For example:

c := make(chan os.Signal)
// signals are sent on c before the channel is read from.
// This signal may be dropped as c is unbuffered.
signal.Notify(c, os.Interrupt)

Users of signal.Notify should use channels with sufficient buffer space to keep up with the expected signal rate.

所以,这并没有直接回答问题,因为它要求一个无缓冲的通道,但如果你想要的只是让它在发送时不阻塞,这就是我想出的:

ch := make(chan shared.BlogPost, 1024)

您输入的数字比预期的要大。如果您事先不知道最大容量,那么此解决方案可能不适合您。

还要记住,go 会急切地分配通道,所以要小心内存使用。