中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go語言之讀寫鎖

發布時間:2020-07-08 12:03:13 來源:網絡 閱讀:1316 作者:baby神 欄目:編程語言

前面的有篇文章在講資源競爭的時候,提到了互斥鎖。互斥鎖的根本就是當一個goroutine訪問的時候,其他goroutine都不能訪問,這樣肯定保證了資源的同步,避免了競爭,不過也降低了性能。


仔細剖析我們的場景,當我們讀取一個數據的時候,如果這個數據永遠不會被修改,那么其實是不存在資源競爭的問題的。因為數據是不變的,不管怎么讀取,多少goroutine同時讀取,都是可以的。


所以其實讀取并不是問題,問題主要是修改。修改的數據要同步,這樣其他goroutine才可以感知到。所以真正的互斥應該是讀取和修改修改和修改之間,讀取和讀取是沒有互斥操作的。


所以這就延伸出來另外一種鎖,叫做讀寫鎖


讀寫鎖可以讓多個讀操作同時并發,同時讀取,但是對于寫操作是完全互斥的。也就是說,當一個goroutine進行寫操作的時候,其他goroutine既不能進行讀操作,也不能進行寫操作。


var count int
var wg sync.WaitGroup
func main() {    wg.Add(10)    for i:=0;i<5;i++ {            go read(i)    }        for i:=0;i<5;i++ {            go write(i);    }    wg.Wait()}func read(n int) {    fmt.Printf("讀goroutine %d 正在讀取...\n",n)    v := count    fmt.Printf("讀goroutine %d 讀取結束,值為:%d\n", n,v)    wg.Done()
}
func write(n int) {    fmt.Printf("寫goroutine %d 正在寫入...\n",n)    v := rand.Intn(1000)    count = v    fmt.Printf("寫goroutine %d 寫入結束,新值為:%d\n", n,v)    wg.Done()
}


以上我們定義了一個共享的資源count,并且聲明了兩個函數readwrite進行讀寫。在main函數的測試中,我們同時啟動了 5 個讀寫goroutine進行讀寫操作,通過打印的結果來看,寫入操作是處于競爭狀態的,有的寫入操作被覆蓋了。通過go build -race也可以看到更明細的競爭態。


針對這種情況,第一個方案是加互斥鎖,同時只能有一個goroutine可以操作count。但是這種方法性能比較慢,而且我們說的讀操作可以不互斥,所以這種情況比較適合使用讀寫鎖。


var count int
var wg sync.WaitGroup
var rw sync.RWMutex
func main() {    wg.Add(10)    for i:=0;i<5;i++ {            go read(i)    }        for i:=0;i<5;i++ {            go write(i);    }    wg.Wait()}func read(n int) {    rw.RLock()    fmt.Printf("讀goroutine %d 正在讀取...\n",n)    v := count    fmt.Printf("讀goroutine %d 讀取結束,值為:%d\n", n,v)    wg.Done()    rw.RUnlock()}func write(n int) {    rw.Lock()    fmt.Printf("寫goroutine %d 正在寫入...\n",n)    v := rand.Intn(1000)    count = v    fmt.Printf("寫goroutine %d 寫入結束,新值為:%d\n", n,v)    wg.Done()    rw.Unlock()
}


我們在read里使用讀鎖,也就是RLockRUnlock,寫鎖的方法名和我們平時使用的一樣,是LockUnlock。這樣,我們就使用了讀寫鎖,可以并發地讀,但是同時只能有一個寫,并且寫的時候不能進行讀操作。現在我們再運行代碼,可以從輸出的數據看到,可以讀到新值了。


我們同時也可以使用go build -race檢測,也沒有競爭提示了。


我們在做Java開發的時候,肯定知道SynchronizedMap這個Map,它是一個在多線程下安全的Map,我們可以通過Collections.synchronizedMap(Map<K, V>)來獲取一個安全的Map。下面我們看看如何使用讀寫鎖,基于Go語言來實現一個安全的Map。


package common
import (    "sync")
//安全的Map
type SynchronizedMap struct {    rw *sync.RWMutex    data map[interface{}]interface{}
}
//存儲操作
func (sm *SynchronizedMap) Put(k,v interface{}){    sm.rw.Lock()    defer sm.rw.Unlock()    sm.data[k]=v
}
//獲取操作
func (sm *SynchronizedMap) Get(k interface{}) interface{}{    sm.rw.RLock()    defer sm.rw.RUnlock()    return sm.data[k]
}
//刪除操作
func (sm *SynchronizedMap) Delete(k interface{}) {    sm.rw.Lock()    defer sm.rw.Unlock()    delete(sm.data,k)
}
//遍歷Map,并且把遍歷的值給回調函數,可以讓調用者控制做任何事情
func (sm *SynchronizedMap) Each(cb func (interface{},interface{})){    sm.rw.RLock()    defer sm.rw.RUnlock()    for k, v := range sm.data {        cb(k,v)    }
}
//生成初始化一個SynchronizedMap
func NewSynchronizedMap() *SynchronizedMap{    return &SynchronizedMap{        rw:new(sync.RWMutex),        data:make(map[interface{}]interface{}),    }
}


這個安全的Map被我們定義為一個SynchronizedMap的結構體,這個結構體里有兩個字段,一個是讀寫鎖rw,一個是存儲數據的datadata是map類型。


然后就是給SynchronizedMap定義一些方法,如果這些方法是增刪改的,就要使用寫鎖;如果是只讀的,就使用讀鎖。這樣就保證了我們數據data在多個goroutine下的安全性。


有了這個安全的Map我們就可以在多goroutine下增刪改查數據了,都是安全的。


這里定義了一個Each方法,這個方法很有意思,用過Gradle的都知道,也有類似遍歷Map的方法。這個方法我們可以傳入一個回調函數作為參數,來對我們遍歷的SynchronizedMap數據進行處理,比如我打印SynchronizedMap中的數據。


sm.Each(func(k interface{}, v interface{}) {
    fmt.Println(k," is ",v)
}


sm就是一個SynchronizedMap,非常簡潔吧。


以上就是讀寫鎖使用的一個例子。我們可以把這個map數據當成緩存數據,或者當成數據庫,然后使用讀寫鎖進行控制,可以多讀,但是只能有一個寫。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

乐山市| 达孜县| 康平县| 仪陇县| 泰兴市| 扎鲁特旗| 仪征市| 达孜县| 上蔡县| 勃利县| 达拉特旗| 江津市| 佛教| 龙岩市| 大关县| 芮城县| 鸡西市| 宁海县| 招远市| 邹城市| 秦安县| 康马县| 临夏市| 金昌市| 景洪市| 博罗县| 沂南县| 视频| 大关县| 南涧| 黎平县| 安吉县| 正宁县| 来宾市| 石家庄市| 河池市| 阜新市| 岳阳市| 长岭县| 白朗县| 鄯善县|