<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 5.3 原子操作 `atomic`包中包含了很多原子型操作。它們均基于運行時中`runtime/internal/atomic`的實現。 ## 5.3.1 原子操作 原子操作依賴硬件指令的支持,但同時還需要運行時調度器的配合。我們以`atomic.CompareAndSwapPointer`為例,介紹`sync/atomic`包提供的同步模式。 `CompareAndSwapPointer`它在包中只有函數定義,沒有函數體: ``` func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) ``` 其本身由運行時實現。 我們簡單看過了兩個屬于公共包的方法`atomic.Value`和`atomic.CompareAndSwapPointer`, 我們來看一下運行時實現: ``` //go:linkname sync_atomic_CompareAndSwapUintptr sync/atomic.CompareAndSwapUintptr func sync_atomic_CompareAndSwapUintptr(ptr *uintptr, old, new uintptr) bool //go:linkname sync_atomic_CompareAndSwapPointer sync/atomic.CompareAndSwapPointer //go:nosplit func sync_atomic_CompareAndSwapPointer(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool { if writeBarrier.enabled { atomicwb(ptr, new) } return sync_atomic_CompareAndSwapUintptr((*uintptr)(noescape(unsafe.Pointer(ptr))), uintptr(old), uintptr(new)) } ``` 可以看到`sync_atomic_CompareAndSwapUintptr`函數在運行時中也是沒有方法本體的, 說明其實現由編譯器完成。那么我們來看一下編譯器究竟干了什么: ``` package main import ( "sync/atomic" "unsafe" ) func main() { var p unsafe.Pointer newP := 42 atomic.CompareAndSwapPointer(&amp;p, nil, unsafe.Pointer(&amp;newP)) v := (*int)(p) println(*v) } ``` 編譯結果: ``` TEXT sync/atomic.CompareAndSwapUintptr(SB) /usr/local/Cellar/go/1.11/libexec/src/sync/atomic/asm.s asm.s:31 0x1001070 e91b0b0000 JMP runtime/internal/atomic.Casuintptr(SB) :-1 0x1001075 cc INT $0x3 (...) TEXT runtime/internal/atomic.Casuintptr(SB) /usr/local/Cellar/go/1.11/libexec/src/runtime/internal/atomic/asm_amd64.s asm_amd64.s:44 0x1001b90 e9dbffffff JMP runtime/internal/atomic.Cas64(SB) :-1 0x1001b95 cc INT $0x3 (...) ``` 可以看到`atomic.CompareAndSwapUintptr`本質上轉到了`runtime/internal/atomic.Cas64`,我們來看一下它的實現: ``` // bool runtime∕internal∕atomic·Cas64(uint64 *val, uint64 old, uint64 new) // Atomically: // if(*val == *old){ // *val = new; // return 1; // } else { // return 0; // } TEXT runtime∕internal∕atomic·Cas64(SB), NOSPLIT, $0-25 MOVQ ptr+0(FP), BX MOVQ old+8(FP), AX MOVQ new+16(FP), CX LOCK CMPXCHGQ CX, 0(BX) SETEQ ret+24(FP) RET ``` 可以看到,實現的本質是使用 CPU 的`LOCK`+`CMPXCHGQ` 指令:首先將 ptr 的值放入 BX,將假設的舊值放入 AX, 要比較的新值放入 CX。然后 LOCK CMPXCHGQ 與累加器 AX 比較并交換 CX 和 BX。 因此原子操作本質上均為使用 CPU 指令進行實現(理所當然)。由于原子操作的方式比較單一,很容易舉一反三, 其他操作不再窮舉。 ## 5.3.2 原子值 原子值需要運行時的支持,在原子值進行修改時,Goroutine 不應該被搶占,因此需要鎖定 MP 之間的綁定關系: ``` //go:linkname sync_runtime_procPin sync.runtime_procPin //go:nosplit func sync_runtime_procPin() int { return procPin() } //go:nosplit func procPin() int { _g_ := getg() mp := _g_.m mp.locks++ return int(mp.p.ptr().id) } //go:linkname sync_atomic_runtime_procUnpin sync/atomic.runtime_procUnpin //go:nosplit func sync_atomic_runtime_procUnpin() { procUnpin() } //go:nosplit func procUnpin() { _g_ := getg() _g_.m.locks-- } ``` 原子值`atomic.Value`提供了一種具備原子存取的結構。其自身的結構非常簡單, 只包含一個存放數據的`interface{}`: type Value struct { v interface{} } ``` 它僅僅只是對要存儲的值進行了一層封裝。要對這個值進行原子的讀取,依賴`Load`方法: ``` func (v *Value) Load() (x interface{}) { // 獲得 interface 結構的指針 // 在 go 中,interface 的內存布局有類型指針和數據指針兩部分表示 vp := (*ifaceWords)(unsafe.Pointer(v)) // 獲得存儲值的類型指針 typ := LoadPointer(&amp;vp.typ) if typ == nil || uintptr(typ) == ^uintptr(0) { return nil } // 獲得存儲值的實際數據 data := LoadPointer(&amp;vp.data) // 將復制得到的 typ 和 data 給到 x xp := (*ifaceWords)(unsafe.Pointer(&amp;x)) xp.typ = typ xp.data = data return } // ifaceWords 定義了 interface{} 的內部表示。 type ifaceWords struct { typ unsafe.Pointer data unsafe.Pointer } ``` 從這個 Load 方法實際上使用了 Go 運行時類型系統中的`interface{}`這一類型本質上由 兩段內容組成,一個是類型 typ 區域,另一個是實際數據 data 區域。 這個 Load 方法的實現,本質上就是將內部存儲的類型和數據都復制一份并返回。 再來看`Store`。存儲的思路與讀取其實是類似的,但由于類型系統的兩段式表示(typ 和 data) 的存在,存儲操作比讀取操作的實現要更加小心,要考慮當兩個不同的 Goroutine 對兩段值進行寫入時, 如何才能避免寫競爭: ``` func (v *Value) Store(x interface{}) { if x == nil { panic("sync/atomic: store of nil value into Value") } // Value 存儲值的指針和要存儲的 x 的指針 vp := (*ifaceWords)(unsafe.Pointer(v)) xp := (*ifaceWords)(unsafe.Pointer(&amp;x)) for { typ := LoadPointer(&amp;vp.typ) // v 還未被寫入過任何數據 if typ == nil { // 禁止搶占當前 Goroutine 來確保存儲順利完成 runtime_procPin() // 先存一個標志位,宣告正在有人操作此值 if !CompareAndSwapPointer(&amp;vp.typ, nil, unsafe.Pointer(^uintptr(0))) { // 如果沒有成功,取消不可搶占,下次再試 runtime_procUnpin() continue } // 如果標志位設置成功,說明其他人都不會向 interface{} 中寫入數據 StorePointer(&amp;vp.data, xp.data) StorePointer(&amp;vp.typ, xp.typ) // 存儲成功,再標志位可搶占,直接返回 runtime_procUnpin() return } // 有其他 Goroutine 正在對 v 進行寫操作 if uintptr(typ) == ^uintptr(0) { continue } // 如果本次存入的類型與前次存儲的類型不同 if typ != xp.typ { panic("sync/atomic: store of inconsistently typed value into Value") } // 類型已經寫入,直接保存數據 StorePointer(&amp;vp.data, xp.data) return } } ``` 可以看到`atomic.Value`的存取通過`unsafe.Pointer(^uintptr(0))`作為第一次存取的標志位, 當`atomic.Value`第一次寫入數據時,會將當前 Goroutine 設置為不可搶占, 并將要存儲類型進行標記,再存入實際的數據與類型。當存儲完畢后,即可解除不可搶占,返回。 在不可搶占期間,且有并發的 Goroutine 再此存儲時,如果標記沒有被類型替換掉, 則說明第一次存儲還未完成,形成 CompareAndSwap 循環進行等待。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看