為了擴大原子操作的適用范圍舰涌,Go語言在1.4版本發(fā)布的時候向sync/atomic包中添加了一個新的類型Value帮辟。此類型的值相當(dāng)于一個容器凝颇,可以被用來“原子地”存儲和加載任意的值婶芭。
atomic.Value類型是開箱即用的纤勒,我們聲明一個該類型的變量(以下簡稱原子變量)之后就可以直接使用了牢裳。這個類型使用起來很簡單逢防,它只有兩個指針方法——Store和Load。不過蒲讯,雖然簡單忘朝,但還是有一些值得注意的地方的。
type ifaceWords struct {
typ unsafe.Pointer
data unsafe.Pointer
}
ifaceWords結(jié)構(gòu)體是實際存儲我們的Value值的地方,可以看到,我們存儲的實際是指向Value的type和data的指針.
Store操作有兩種行為模式:
1.First Store : 當(dāng)我們第一次調(diào)用Store的時候,Store函數(shù)會初始化typ指針(需要注意的是,每一個Value在第一次Store之后typ就被確定而不能更改了,否則會panic).
2.后面的每次Store調(diào)用都是直接替換掉data指針
Load函數(shù)檢測typ的值,如果為nil或者正在進行首次調(diào)用Store則會返回nil.否則返回一個interface{}(實際存儲的是ifaceWords值)
使用規(guī)則
第一條規(guī)則判帮,不能用原子值存儲nil
我們不能把nil作為參數(shù)值傳入原子值的Store方法局嘁,否則就會引發(fā)一個panic。
這里要注意晦墙,如果有一個接口類型的變量悦昵,它的動態(tài)值是nil,但動態(tài)類型卻不是nil晌畅,那么它的值就不等于nil但指。這樣一個變量的值是可以被存入原子值的。
第二條規(guī)則抗楔,我們向原子值存儲的第一個值棋凳,決定了它今后能且只能存儲哪一個類型的值
第一次向一個原子值存儲了一個string類型的值,那我在后面就只能用該原子值來存儲字符串了连躏。如果我又想用它存儲結(jié)構(gòu)體剩岳,那么在調(diào)用它的Store方法的時候就會引發(fā)一個panic。這個panic會告訴我入热,這次存儲的值的類型與之前的不一致拍棕。
Value值內(nèi)部是依據(jù)被存儲值的實際類型來做判斷的晓铆,因此想通過接口類型的值,然后再存儲這個接口的某個實現(xiàn)類型的值這樣的方式是不可行的莫湘。
使用建議
1.不要把內(nèi)部使用的原子值暴露給外界尤蒿。比如,聲明一個全局的原子變量并不是一個正確的做法幅垮。這個變量的訪問權(quán)限最起碼也應(yīng)該是包級私有的腰池。
2.如果不得不讓包外,或模塊外的代碼使用你的原子值忙芒,那么可以聲明一個包級私有的原子變量示弓,然后再通過一個或多個公開的函數(shù),讓外界間接地使用到它呵萨。注意奏属,這種情況下不要把原子值傳遞到外界,不論是傳遞原子值本身還是它的指針值潮峦。
3.如果通過某個函數(shù)可以向內(nèi)部的原子值存儲值的話囱皿,那么就應(yīng)該在這個函數(shù)中先判斷被存儲值類型的合法性。若不合法忱嘹,則應(yīng)該直接返回對應(yīng)的錯誤值嘱腥,從而避免panic的發(fā)生。
4.如果可能的話拘悦,我們可以把原子值封裝到一個數(shù)據(jù)類型中齿兔,比如一個結(jié)構(gòu)體類型。這樣础米,我們既可以通過該類型的方法更加安全地存儲值分苇,又可以在該類型中包含可存儲值的合法類型信息。