這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
對於複雜類型如 container/list ,需要在所有讀寫操作上使用 sync.mutex 互斥鎖以保證資料一致性,互斥鎖並發情況下,Lock 操作會阻塞,一直等到其他線程Unlock,但是有的時候因為有一個耗時比較長的操作一直佔用鎖,我們想讓其他線程不在Lock上一直阻塞,而是直接走其他商務程序。
舉一個很簡單的情境:在多人並發抽獎環節,為了保證不出現負庫存,我們可以通過競爭鎖,第一個擷取鎖的人可能中獎,而其他並發過來的請求擷取鎖失敗(而不是一直阻塞在Lock()),直接當做未中獎處理。
思考了一下,有一個思路,我們可以用兩道鎖,第一道鎖用來判斷鎖狀態,第二道鎖才是真正的耗時任務用的鎖。
直接上代碼
type NBLocker struct{l1 sync.Mutexl2 sync.Mutexlocked bool}func (NBLocker *NBLocker) Lock() (success bool) {NBLocker.l1.Lock()defer NBLocker.l1.Unlock()if NBLocker.locked == false {NBLocker.locked = truesuccess = trueNBLocker.l2.Lock()}return}func (NBLocker *NBLocker) Unlock() {NBLocker.l1.Lock()defer NBLocker.l1.Unlock()NBLocker.locked = falseNBLocker.l2.Unlock()}
使用
type foo struct {mux NBLocker}func (self *foo) Bong(wg *sync.WaitGroup) {defer wg.Done()if !self.mux.Lock() {fmt.Println("擷取鎖失敗")return}defer self.mux.Unlock()time.Sleep(time.Second) //停頓一秒fmt.Println("bong~")}func main() {f := &foo{}wg := &sync.WaitGroup{}wg.Add(4)go f.Bong(wg)go f.Bong(wg)go f.Bong(wg)time.Sleep(time.Second *2)go f.Bong(wg)wg.Wait()}
如果擷取第二道鎖失敗,NBLocker.Lock() 方法會直接返回false,這時候只需要判斷一下就可以直接跳過"抽獎環節"
以上常式輸出
擷取鎖失敗擷取鎖失敗bong~bong~