golang 中的 poolDequeue 是 ring buffer 的一种实现. ring buffer 是一种无锁循环队列。这里的 poolDequeue 仅支持1个生产者,但可以有多个消费者(通过 CAS 实现),因为无锁,所以性能非常好。ring buffer 这里不展开,网上很多资料。
这里重点通过 eface 和 dequeueNil 来阐明golang 中的 interface{} 是由 type 和 value 2个机器字组成。当 type 和 value 都为nil(即null ) 时, 才为 nil 的interface;当 type!=nil,value==nil 时,不是 nil 的interface.
package main
import (
"fmt"
"reflect"
"sync/atomic"
"unsafe"
)
type eface struct {
typ, val unsafe.Pointer
}
// dequeueNil is used in poolDeqeue to represent interface{}(nil).
// Since we use nil to represent empty slots, we need a sentinel value
// to represent nil.
type dequeueNil *struct{}
func main() {
// dnil 作为 *struct{} 确实为nil 即 null 指针,1个机器字,但是
// 当它转为 interface{} 后,2个机器字,不是 nil 的 interface
// dnil type: main.dequeueNil , value: <nil>
dnil := dequeueNil(nil)
fmt.Println("dnil type:", reflect.TypeOf(dnil),
", value:", reflect.ValueOf(dnil))
// dnil == nil 成立
if dnil == nil {
fmt.Println("dnil == nil")
} else {
fmt.Println("dnil != nil")
}
var dnil2 interface{} = dnil
fmt.Println("dnil2 type:", reflect.TypeOf(dnil2),
", value:", reflect.ValueOf(dnil2))
// dnil2 != nil 成立
if dnil2 != nil {
fmt.Println("dnil2 != nil")
} else {
fmt.Println("dnil2 == nil")
}
// slot 是非空指针, 转成 interface{} 没有问题;但是
// 由于 eface{} 的两个指针 typ, val 都为空指针
// 因此等价于 interface{} 的 type vaule 都为 null,因
// 此转成 interface{} 后就是 nil 的 interface{}
// 因此这里解开了神秘的 interface{} 的,interface{} 实
// 际上就是含有 type, value 指针的一个结构体。
// 当 interface{} 的 type 和value 同时为 null 指针时,
// 代表 nil interface.
var slot2 *eface = &eface{}
nilInterface := *(*interface{})(unsafe.Pointer(slot2))
fmt.Println("unsafe.Pointer(slot)", unsafe.Pointer(slot2))
fmt.Println("nilInterface type:", reflect.TypeOf(nilInterface),
", value:", reflect.ValueOf(nilInterface))
// 相等
if nilInterface == nil {
fmt.Println("nilInterface == nil")
} else {
fmt.Println("nilInterface != nil")
}
// 不相等, 因为 dequeueNil(nil) 转成 interface 后 type 不为 null 指针
if nilInterface == dequeueNil(nil) {
fmt.Println("nilInterface == dequeueNil(nil)")
} else {
fmt.Println("nilInterface != dequeueNil(nil)")
}
// Initialize the chain.
const initSize = 8 // Must be a power of 2
vals := make([]eface, initSize)
// 现在我来尝试回答作者为什么要使用 dequeueNil(nil) 来作为
// interface{}(nil) 即 nil interface 的代表呢? 正如注释
// 所说的那样,因为我们使用 nil interface 代表
// slot 是空的 (用户没放任何东西,可以使用). 如果外部 push
// 一个 nil interface 进来, 如果直接保存,那么会以为这个
// slot 是空的,仍然可以使用。但是如果此时保存的是 dequeueNil(nil),
// dequeueNil(nil) 转成 interface 后是 type 不为null, value
// 为null 的非 nil interface, 就可以区别开来了.
pushHead(vals, 0, nil)
if v2, ok2 := popHead(vals, 0); ok2 {
fmt.Println("v2:", v2)
} else {
fmt.Println("not ok")
}
}
func pushHead(vals []eface, head uint32, val interface{}) {
slot := &vals[head&uint32(len(vals)-1)]
// Check if the head slot has been released by popTail.
typ := atomic.LoadPointer(&slot.typ)
if typ != nil {
// Another goroutine is still cleaning up the tail, so
// the queue is actually still full.
return
}
// 问题,如果把下面这段 if 代码注释, 然后执行
// pushHead(vals, 0, nil), popHead(vals, 0)
// popHead 中的 val := *(*interface{})(unsafe.Pointer(slot))
// 会发生空指针非法访问吗? 答案是不会,因为
// *(*interface{})(unsafe.Pointer(slot)) = nil
// 只是等价于 slot.typ = null, slot.val = null,
// 也等价于 *slot = eface{}
// The head slot is free, so we own it.
if val == nil {
val = dequeueNil(nil)
}
// 等价于 slot.typ = val.type, slot.val = val.value
*(*interface{})(unsafe.Pointer(slot)) = val
}
func popHead(vals []eface, head uint32) (interface{}, bool) {
var slot *eface
slot = &vals[head&uint32(len(vals)-1)]
val := *(*interface{})(unsafe.Pointer(slot))
if val == dequeueNil(nil) {
val = nil
}
// Zero the slot. Unlike popTail, this isn't racing with
// pushHead, so we don't need to be careful here.
*slot = eface{}
return val, true
}