在监听和服务时使用缓冲通道处理错误

Using buffered channel for errors in go when listening and serving

我正在阅读有关使用 go 构建 Web 服务器的教程。

作者没有直接使用 http.ListenAndServe() 方法,而是创建了 http.Server 结构。

然后他继续:

根据讲师的说法,使用缓冲通道的原因

so that the goroutine can exit if we do not collect this error

确实在下面的程序中有一个select块,正在收集来自该频道的错误。

谁能帮我理解如果我们不收集错误,goroutine 是如何退出的?

如果我们使用无缓冲通道,实际会有什么不同?

简答:

对于任何通道(缓冲或未缓冲),如果没有任何内容写入通道,通道读取会阻塞。

对于非缓冲通道,通道 writes 将在没有人收听时阻塞。

这是错误通道的一种常见技术(因为只有一个项目会被写入通道),使其成为大小为 1 的缓冲通道。它确保写入不会阻塞 - 并且写入器goroutine 可以继续前进 return.

因此,该服务不依赖于客户端调用者从错误通道中读取来执行其清理。

注意:要回收通道重新 GC,它只需要超出范围 - 它不需要完全耗尽。也不需要关闭。一旦它从两端超出范围,它将被 GC。

如果您参考 ListenAndServe() 的代码,您会注意到以下关于其工作原理的注释。引用自那里:

// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.

此外,

// When Shutdown is called, Serve, ListenAndServe, and
// ListenAndServeTLS immediately return ErrServerClosed. Make sure the
// program doesn't exit and waits instead for Shutdown to return.


您的 select 块正在等待 Shutdown(错误)考虑到您正在优雅地处理服务器的关闭并且不让 goroutine 在它优雅地关闭之前退出。

func (srv *Server) Close()的情况下(例如,大多数使用defer srv.Close(),对吧?)

// Close immediately closes all active net.Listeners and any
// connections in state StateNew, StateActive, or StateIdle. For a

// Close returns any error returned from closing the Server's
// underlying Listener(s).
// graceful shutdown, use Shutdown.

因此,与上面相同的解释是使用 select 块。


现在,让我们将通道分类为 bufferedunbuffered,如果我们确实关心信号传递的保证(与通道的通信),那么无缓冲通道可以确保这一点。然而,如果缓冲通道(大小 = 1)符合您的情况,那么它可以确保交付但可能会延迟。

让我们详细说明unbuffered channels:

A send operation on an unbuffered channel blocks the sending goroutine until another 
goroutine executes a corresponding receive on that same channel, at which point the value 
is transmitted and both goroutines may continue

Conversely, if received on the channel earlier (<-chan) than send operation, then the 
receiving goroutine is blocked until the corresponding send operation occurs on the 
same channel on another goroutine.

Aforementioned points for unbuffered channels indicate synchronous nature.

记住,func main() 也是一个 goroutine。

让我们详细说明buffered channels:

A send operation on a buffered channel pushes an element at the back of the queue, 
and a receive operation pops an element from the front of the queue. 
 1. If the channel is full, the send operation blocks its goroutine until space is made available by another goroutine's receive. 
 2. If the channel is empty, a receive operation blocks until a value is sent by another goroutine.

So in your case size of the channel is 1. The other sender goroutine can send in a non-blocking manner as the receiver channel of the other goroutine dequeues it as soon as it receives. But, if you remember, I mentioned delayed delivery for the channel with size 1 as we don't how much time it'll take for the receiver channel goroutine to return.

因此,要阻止发送者 goroutine,使用 select block。从引用代码的文档中,您可以看到

// Make sure the program doesn't exit and waits instead for Shutdown to return.

另外,为了更清楚,您可以参考:Behaviour of channels 作者解释得很清楚。