概述
这里一个介绍一个方法,可以在不取channel里的数据的前提下,查看channel是否关闭,是否阻塞,缓冲大小,通道内当前缓冲数据量等。
在这里需要了解一下golang的内存模型,然后通过指针取出相应的值。
channel的结构体在chan.go
中:
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
//... 以下字段没有用上,先省略
}
从上面可以看出,现在要从出取出:
-
qcount
:queue里的数据总数 -
dataqsiz
:queue的最大长度 -
closed
:是否关闭
实现
首先我们定义一个struct来与hchan对应。(因为hchan里的字段长度不定,qcount
、dataqsiz
都是uint
还好说,主要是取closed中间隔了个unint16
,所以这里偷个懒)
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
}
省略字段这里没用到,所以直接去掉就行了,上面的属性顺序和类型要与原hchan
必须保持一致。
这里想把它通用化所以包成方法接收的会是个interface{}
,如里channel给interface{}
那它在golang底层会被解析成eface
:
type eface struct {
_type *_type
data unsafe.Pointer
}
所以先将指针定位到eface.data
再将指向的数据空间转为自定义的hchan
:
i := (*[2]uintptr)(unsafe.Pointer(&c))
h := (*hchan)(unsafe.Pointer(i[1]))
这时h
中值已经注入了。
附录
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
}
type ChanInfo struct {
Closed bool // 是否关闭
Len uint // channel内数据量
Cap uint // channel容量
Block bool // 是否已经阻塞
}
func ChanStatus(c interface{}) (*ChanInfo, error) {
v := reflect.ValueOf(c)
if v.Type().Kind() != reflect.Chan {
return nil, errors.New("type must be channel")
}
i := (*[2]uintptr)(unsafe.Pointer(&c))
h := (*hchan)(unsafe.Pointer(i[1]))
return &ChanInfo{
Cap: h.dataqsiz,
Len: h.qcount,
Closed: h.closed == 1,
Block: h.qcount >= h.dataqsiz,
}, nil
}