poolDequeue 的 eface 和 dequeueNil

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
}


下一篇 随机行走

欢迎关注我的微信公众号[数学345]:长按"识别图中二维码";或打开微信扫一扫。

评论(0)