1. 程式人生 > 其它 >可重入鎖的學習

可重入鎖的學習

技術標籤:golang多執行緒

java和Go在可重入鎖上的對比

面試提到有關go是如何實現可重入鎖的,都不太記得go有這個,記錄下

  1. 可重入鎖的概念:指的是同一個執行緒外層函式獲得鎖之後,內層遞迴函式仍然能獲取該鎖的程式碼,在同一個執行緒在外層方法獲取鎖的時候,在進入內層方法會自動獲取鎖。

  2. java的可重入鎖:Java直接展示結果,是可以執行的,類似的程式碼結構在golang中會出現 競爭異常(java的synchronized也是可重入結構)
    在這裡插入圖片描述

  3. go的鎖是否是可重入的

    type Reentrant struct {
    	sync.Mutex
    
    }
    
    func (receiver *
    Reentrant) methodA() { receiver.Lock() fmt.Println("method A is running") receiver.methodB() receiver.Unlock() } func (receiver Reentrant) methodB() { receiver.Lock() fmt.Println("method B is running") receiver.Unlock() } func TestReentrant(t *testing.T) { a := new(Reentrant)
    a.methodA() }

    在這裡插入圖片描述

  4. go實習一個簡單的可重入鎖
    關於可重入鎖的原理,需要儲存的資訊包括鎖定狀態(需要注意狀態設定的原子性),持有鎖的執行緒,以及重入的次數,其中針對go來說獲取執行緒編號存在問題

    	import (
    	"fmt"
    	"runtime"
    	"strconv"
    	"strings"
    	"sync"
    	"sync/atomic"
    )
    
    type MyReentrantLock struct {
    	lock sync.Mutex
    	id int
    	counter int32
    } func (l *MyReentrantLock) Lock() { // 第一次鎖 if atomic.CompareAndSwapInt32(&(l.counter), 0, 1){ l.lock.Lock() l.id = GoID() }else { if GoID() == l.id{ atomic.AddInt32(&(l.counter), 1) }else { // 這裡就是阻塞goroutine了 l.lock.Lock() } } } func (l *MyReentrantLock) Unlock() { // 當counter歸零是真正釋放鎖 v := atomic.LoadInt32(&(l.counter)) if v == 0{ panic("this mutex is not locked") } if l.id == GoID(){ v := atomic.AddInt32(&(l.counter), -1) if v == 0 { l.lock.Unlock() } } } func GoID() int { var buf [64]byte n := runtime.Stack(buf[:], false) idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0] id, err := strconv.Atoi(idField) if err != nil { panic(fmt.Sprintf("cannot get goroutine id: %v", err)) } return id }

參考連結

  1. 對可重入鎖和不可重入鎖的理解,他們的區別及實現原理解析
  2. 如何獲取goroutine id