通道(channel)是一种通过信号在goroutine之间进行通信的机制。信号可以有数据也可以没有数据,但是对于Go开发者来说,处理没有数据的情况并不是那么简单。下面通过一个具体的例子来深入研究它,在程序中,会创建一个通道,该通道将在发生特定断开连接时发生通知,一种处理的思路是定义一个chan bool类型的通道。
disconnectCh := make(chan bool)
现在假设我们与这个通道的API进行交互,由于它是一个bool类型的通道,可以从里面接收真消息或假消息。如果收到true我们可能很清楚表达的含义是断开了连接,但是当收到false的时候,表达的是什么意思呢?表示的是我们没有断开连接吗?在这种情况下,多久我们将收到一次这样的信号?这是不是意味着我们已经进行了重新连接?也许,我们应该只期望收到为true的消息。
如果是上面这样的情况下,意味着我们不需要一个特定的值来传递信息,我们需要的是一个不需要发送数据的通道。这种通道有一种惯用方式是定义为chan struct{}
类型。在Go语言中,空结构体struct{}
不包含任何字段,无论在哪种计算机体系结构下,它占用的存储空间为零字节。可以使用unsafe.Sizeof
进行验证,下面的程序执行结果输出0.
var s struct{}
fmt.Println(unsafe.Sizeof(s))
上面的程序打印输出结果如下
0
「NOTE: 也许有人好奇会问那为啥不使用空接口var i interface{}
, 因为空接口占用的存储空间不是零字节。在32系统下,它占用8字节,在64位系统下,它占用16字节。」
事实上,在不关心值的时候,使用空结构体已成为一种标准用法。例如,当我们需要一个哈希结构时,只关心哈希的键不关心值,这个时候,我们应该使用一个空结构体作为值,例如map[string]struct{}
。
在使用通道的时候,如果我们想要创建一个通道来发送通知,并不发送数据。在Go中合适的方式是使用chan struct{}
. 空结构体通道最广为人知的使用方式之一是在标准库context包。下面是从context包摘录的一个例子。
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
总结,一个通道可以有或没有数据,如果想设计一个符合Go标准惯用API, 在通道不传递实际数据的时候,我们应该将通道定义为chan struct{}
类型。通过这种显示的定义方式,向接收者表明,他们不要期望消息内容有任何意义,只希望他们收到消息这一事实。这种类型的通道应该作为通知通道使用。