<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>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] # 簡介 * 加鎖代價比較耗時,需要上下文切換 * 針對基本數據類型,可以使用原子操作保證線程安全 * 原子操作在用戶態就可以完成,因此性能比互斥鎖要高 sync/atomic包中的函數可以做的原子操作有: * 加法(add) * 比較并交換(compare and swap,簡稱 CAS) * 加載(load) * 存儲(store) * 交換(swap) 原子操作函數需要的是被操作值的指針,而不是這個值本身 **只要原子操作函數拿到了被操作值的指針**,就可以定位到存儲該值的內存地址。只有這樣,它們才能夠通過底層的指令,準確地操作這個內存地址上的數據。 # 支持的類型 這些函數針對的數據類型并不多。但是,對這些類型中的每一個, sync/atomic包都會有一套函數給予支持。這些數據類型有:int32、int64、uint32、uint64、uintptr,以及unsafe包中的Pointer。 不過,針對unsafe.Pointer類型,該包并未提供進行原子加法操作的函數。 # 減法 atomic.AddInt32函數的第二個參數代表差量,它的類型是int32,是有符號的。如果我們想做原子減法,那么把這個差量設置為負整數就可以了。 對于atomic.AddInt64函數來說也是類似的。不過,要想用atomic.AddUint32和atomic.AddUint64函數做原子減法,就不能這么直接了,因為它們的第二個參數的類型分別是uint32和uint64,都是無符號的,不過,這也是可以做到的,就是稍微麻煩一些。 例如,如果想對uint32類型的被操作值18做原子減法,比如說差量是\-3,那么我們可以先把這個差量轉換為有符號的int32類型的值,然后再把該值的類型轉換為uint32,用表達式來描述就是uint32(int32(-3))。 不過要注意,直接這樣寫會使 Go 語言的編譯器報錯,它會告訴你:“常量\-3不在uint32類型可表示的范圍內”,換句話說,這樣做會讓表達式的結果值溢出。 不過,如果我們先把int32(-3)的結果值賦給變量delta,再把delta的值轉換為uint32類型的值,就可以繞過編譯器的檢查并得到正確的結果了。 最后,我們把這個結果作為atomic.AddUint32函數的第二個參數值,就可以達到對uint32類型的值做原子減法的目的了。 還有一種更加直接的方式。我們可以依據下面這個表達式來給定atomic.AddUint32函數的第二個參數值: ~~~ ^uint32(-N-1)) ~~~ 其中的N代表由負整數表示的差量。也就是說,我們先要把差量的絕對值減去1,然后再把得到的這個無類型的整數常量,轉換為uint32類型的值,最后,在這個值之上做按位異或操作,就可以獲得最終的參數值了。 這么做的原理也并不復雜。簡單來說,此表達式的結果值的補碼,與使用前一種方法得到的值的補碼相同,所以這兩種方式是等價的。我們都知道,整數在計算機中是以補碼的形式存在的,所以在這里,結果值的補碼相同就意味著表達式的等價 # 比較并交換和交換操作相比有什么不同? 比較并交換操作即 CAS 操作,是有條件的交換操作,只有在條件滿足的情況下才會進行值的交換。 所謂的交換指的是,把新值賦給變量,并返回變量的舊值。 在進行 CAS 操作的時候,函數會先判斷被操作變量的當前值,是否與我們預期的舊值相等。如果相等,它就把新值賦給該變量,并返回true以表明交換操作已進行;否則就忽略交換操作,并返回false。 可以看到,CAS 操作并不是單一的操作,而是一種操作組合。這與其他的原子操作都不同。正因為如此,它的用途要更廣泛一些。例如,我們將它與for語句聯用就可以實現一種簡易的自旋鎖(spinlock)。 ~~~ for { if atomic.CompareAndSwapInt32(&num2, 10, 0) { fmt.Println("The second number has gone to zero.") break } time.Sleep(time.Millisecond * 500) } ~~~ for語句加 CAS 操作的假設往往是:共享資源狀態的改變并不頻繁,或者,它的狀態總會變成期望的那樣。這是一種更加樂觀,或者說更加寬松的做法。 # 方法 目前只支持int類型 ![](https://box.kancloud.cn/8ae6837c756e60b10e9cdae283bc827e_769x781.png) ~~~ import ( "fmt" "sync" "sync/atomic" ) var x int32 var wg sync.WaitGroup func add() { for i := 0; i<5000; i++ { //x = x+1 atomic.AddInt32(&x, 1) } defer wg.Done() } func main() { wg.Add(2) //各加5000 go add() go add() wg.Wait() //結果一定是10000 fmt.Println(x) } ~~~ # 自旋鎖 ~~~ // forAndCAS1 用于展示簡易的自旋鎖。 func forAndCAS1() { sign := make(chan struct{}, 2) num := int32(0) fmt.Printf("The number: %d\n", num) go func() { // 定時增加num的值。 defer func() { sign <- struct{}{} }() for { time.Sleep(time.Millisecond * 500) newNum := atomic.AddInt32(&num, 2) fmt.Printf("The number: %d\n", newNum) if newNum == 10 { break } } }() go func() { // 定時檢查num的值,如果等于10就將其歸零。 defer func() { sign <- struct{}{} }() for { if atomic.CompareAndSwapInt32(&num, 10, 0) { fmt.Println("The number has gone to zero.") break } time.Sleep(time.Millisecond * 500) } }() <-sign <-sign } ~~~ # atomic.value atomic.Value分為兩個操作,通過Store()存儲Value,通過Load()來讀取Value的值. ~~~ package main import ( "fmt" "sync/atomic" ) type Value struct { key string Val interface{} } type Noaway struct { Movies atomic.Value Total atomic.Value } func NewNoaway() *Noaway { n := new(Noaway) n.Movies.Store(&Value{key: "movie", Val: "Wolf Warrior 2"}) n.Total.Store("123") return n } func main() { n := NewNoaway() val := n.Movies.Load().(*Value) total := n.Total.Load().(string) fmt.Printf("%#v --- %#v\n", val, total) } ~~~ ## 如何用好atomic.value 第一條規則,不能用原子值存儲nil。 也就是說,我們不能把nil作為參數值傳入原子值的Store方法,否則就會引發一個 panic。 這里要注意,如果有一個接口類型的變量,它的動態值是nil,但動態類型卻不是nil,那么它的值就不等于nil。我在前面講接口的時候和你說明過這個問題。正因為如此,這樣一個變量的值是可以被存入原子值的。 第二條規則,我們向原子值存儲的第一個值,決定了它今后能且只能存儲哪一個類型的值。 例如,我第一次向一個原子值存儲了一個string類型的值,那我在后面就只能用該原子值來存儲字符串了。如果我又想用它存儲結構體,那么在調用它的Store方法的時候就會引發一個 panic。這個 panic 會告訴我,這次存儲的值的類型與之前的不一致。 你可能會想:我先存儲一個接口類型的值,然后再存儲這個接口的某個實現類型的值,這樣是不是可以呢? 很可惜,這樣是不可以的,同樣會引發一個 panic。因為原子值內部是依據被存儲值的實際類型來做判斷的。所以,即使是實現了同一個接口的不同類型,它們的值也不能被先后存儲到同一個原子值中。 遺憾的是,我們無法通過某個方法獲知一個原子值是否已經被真正使用,并且,也沒有辦法通過常規的途徑得到一個原子值可以存儲值的實際類型。這使得我們誤用原子值的可能性大大增加,尤其是在多個地方使用同一個原子值的時候。 下面,我給你幾條具體的使用建議。 1. 不要把內部使用的原子值暴露給外界。比如,聲明一個全局的原子變量并不是一個正確的做法。這個變量的訪問權限最起碼也應該是包級私有的。 2. 如果不得不讓包外,或模塊外的代碼使用你的原子值,那么可以聲明一個包級私有的原子變量,然后再通過一個或多個公開的函數,讓外界間接地使用到它。注意,這種情況下不要把原子值傳遞到外界,不論是傳遞原子值本身還是它的指針值。 3. 如果通過某個函數可以向內部的原子值存儲值的話,那么就應該在這個函數中先判斷被存儲值類型的合法性。若不合法,則應該直接返回對應的錯誤值,從而避免 panic 的發生。 4. 如果可能的話,我們可以把原子值封裝到一個數據類型中,比如一個結構體類型。這樣,我們既可以通過該類型的方法更加安全地存儲值,又可以在該類型中包含可存儲值的合法類型信息。 除了上述使用建議之外,我還要再特別強調一點:盡量不要向原子值中存儲引用類型的值。因為這很容易造成安全漏洞。請看下面的代碼: ~~~ var box6 atomic.Value v6 := []int{1, 2, 3} box6.Store(v6) v6[1] = 4 // 注意,此處的操作不是并發安全的! ~~~ 我把一個[]int類型的切片值v6, 存入了原子值box6。注意,切片類型屬于引用類型。所以,我在外面改動這個切片值,就等于修改了box6中存儲的那個值。這相當于繞過了原子值而進行了非并發安全的操作。那么,應該怎樣修補這個漏洞呢?可以這樣做: ~~~ store := func(v []int) { replica := make([]int, len(v)) copy(replica, v) box6.Store(replica) } store(v6) v6[2] = 5 // 此處的操作是安全的 ~~~
                  <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>

                              哎呀哎呀视频在线观看