Go小技巧(二)— 打開已經(jīng)關閉的channel

概述

有時候我們需要在完全可控的范圍內(nèi)復用channel,但是關閉了的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是否關閉取決于hchan.closed,0是打開,1是關閉。
方法:讓指針指向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值,直接修改標志位。為了保證設置closed值的安全性所以在給它設置值的時候使用runtime.lock上鎖。
關于go:linkname可以自行百度,在目錄下創(chuàng)建一個*.s文件可以躲過編譯時error:missing function body。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容