在經過幾個package都看到位移處理的寫法,終於找個時間用心研究一下,一切都是為了能夠更一進步了解某些package的內容。
以sync裡面的mutex為例,有段程式碼如下
const (
mutexLocked = 1 << iota
mutexWoken
mutexStarving
mutexWaiterShift = iota
)
一開始看得時候根本一頭霧水。
『<<』和『>>』是位移處理的動作,看到這兩個符號分別是表示『左位移』和『右位移』。
而且這個位移都是2進位的處理,沒有其他進制了。
如int32型態,其實表示轉成2進位,有32個位元,也等於有32個bit的意思。
十進位的1 = 2進制的 00000000000000000000000000000001
十進位的2 = 2進制的 00000000000000000000000000000010
十進位的3 = 2進制的 00000000000000000000000000000011
十進位的4 = 2進制的 00000000000000000000000000000100
上面的範例code還加上iota這個小東西,這是golang的關鍵字,意思是根據規則,往下照順序排,最初始值為1,而且不重複。
先不看位元運算的話
const (
a1 = iota
a2
a3
a4
a5
)
a1 到a5分別會是 0、1、2、3、4。
那如果搭配位元運算呢?
1 << iota 表示每次往左移一位
所以實際上數值會是這樣子
const (
mutexLocked = 1 << iota // 1 (00001)
mutexWoken // 2 (00010)
mutexStarving // 4 (00100)
mutexWaiterShift = iota // 3 因為回歸正常,1和2已經用過,所以從3開始
)
有時候一個業務處理,甚至說是一個比較複雜處理的運算核心,往往會需要許多狀態來紀錄。
若每個狀態都開一個變數來紀錄,其實非常麻煩。
若狀態的表示只是像開關,不是開就是關,不是1就是0,那其實就像伯努利分布一樣。
2進位的表示方式,每個bit不是1就是0,所以這個方法,是把每個bit是不互相影響的個體,每個bit主管一個狀態的開關。
1代表開,0代表關,同時可以存在很多bit是開的狀態,或者多個同是關的狀態。
最後,再加上一個很重要的關鍵,如果把這個核心的所有狀態放在一個變數就可以處理或變更。
那我使用lock的效能就非常優異,實際上sync這個package使用了atomic的方式,來對這個變數,也就是這整個核心的狀態做變更,達到原子性的目的。