概述
有時候我們需要在完全可控的范圍內(nèi)復(fù)用channel扇单,但是關(guān)閉了的channel原生語法并沒有提供方法打開,所以利用指針再次打開赠群。
channel的結(jié)構(gòu)體在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
//... 以下字段沒有用上褒搔,先省略
}
Channel是否關(guān)閉取決于hchan.closed
,0是打開履肃,1是關(guān)閉。
方法:讓指針指向hchan.closed
直接修改它的值坐桩。
代碼實現(xiàn)
//go:linkname lock runtime.lock
func lock(l *mutex)
//go:linkname unlock runtime.unlock
func unlock(l *mutex)
func open(c interface{}) error {
v := reflect.ValueOf(c)
if v.Type().Kind() != reflect.Chan {
return errors.New("type must be channel")
}
i := (*[2]uintptr)(unsafe.Pointer(&c)) //定位c所在數(shù)據(jù)空間尺棋,這里的c是個指針所以要進行一步取值
var closedOffset, lockOffset uintptr = 28, 88
closed := (*uint32)(unsafe.Pointer(i[1] + closedOffset)) //指向closed的地址
if *closed == 1 {
lockPtr := (*mutex)(unsafe.Pointer(i[1] + lockOffset)) //指向lock地址
lock(lockPtr) //上鎖
if *closed == 1 {
*closed = 0 //直接修改值
}
unlock(lockPtr) //解鎖
}
return nil
}
closedOffset為什么是28呢?這個涉及到struct對齊問題绵跷,Go內(nèi)存優(yōu)化(一)— struct對齊
在上面主要用了指針定位closed值膘螟,直接修改標志位。為了保證設(shè)置closed值的安全性所以在給它設(shè)置值的時候使用runtime.lock上鎖抖坪。
關(guān)于go:linkname
可以自行百度萍鲸,在目錄下創(chuàng)建一個*.s
文件可以躲過編譯時error:missing function body
。