通道是一种通过信号在 goroutine 之间进行通信的机制。信号可以有或没有数据。然而,对于 Go 程序员来说,如何处理后一种情况并不总是直截了当的。
让我们用一个具体的例子来深入研究它。我们将创建一个通道,该通道将在发生特定断开连接时进行通知。一个想法可能是将其作为 chan bool
处理:
disconnectCh := make(chan bool)
现在,假设您与一个为您提供此类通道的 API 进行交互。由于它是一个布尔通道,我们可以接收真或假消息。可能很清楚 true
传达了什么。然而,false 是什么意思?这是否意味着我们没有断开连接,在这种情况下,我们多久会收到这样的信号?这是否意味着我们已经重新连接?我们甚至应该期望收到 false
吗?也许,我们应该只期望收到 true
的信息。
如果是这样的话,意味着我们不需要特定的值来传达一些信息,我们需要一个 不携带 数据的通道。
处理它的惯用方法是空结构的通道:chan struct{}
。
在 Go 中,空结构是没有任何字段的结构。无论架构如何,它都占用零字节的存储空间,我们可以使用 unsafe.Sizeof
进行验证:
var s struct{}
fmt.Println(unsafe.Sizeof(s))
0
Note 我们可能想知道为什么不使用空接口(
var i interface{}
)。然而,一个空的接口实际上不是真的空。它在 32 位架构上占用 8 个字节,在 64 位架构 上占用 16 个字节。
空结构是传达缺乏意义的事实上的标准。例如,如果我们需要一个哈希集结构(唯一元素的集合),我们应该使用一个空结构作为值(例如,map[string]struct{}
)。
对于通道,如果我们想创建一个通道来发送没有数据的通知,在 Go 中合适的方式是 chan struct{}
。空结构通道最广为人知的使用方式之一是 Go 上下文,我们将在本章中深入探讨。
一个通道可以有或没有数据。如果我们想设计一个符合 Go 标准的惯用 API,让我们记住一个没有数据的通道应该用 chan struct{}
类型表示。通过这种方式,它向接收者阐明了他们不期望消息内容有任何意义,只期望他们收到消息的事实。此类通道在 Go 通知通道中被调用。
在下一节中,让我们讨论 Go 如何处理 nil 通道以及使用它们的基本原理。