<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
專案中遇到了可重入鎖的需求和實現,具體記錄下。
我們平時說的分散式鎖,一般指的是在不同伺服器上的多個執行緒中,只有一個執行緒能搶到一個鎖,從而執行一個任務。而我們使用鎖就是保證一個任務只能由一個執行緒來完成。所以我們一般是使用這樣的三段式邏輯:
Lock();
DoJob();
Unlock();
但是由於我們的系統都是分散式的,這個鎖一般不會只放在某個程序中,我們會借用第三方儲存,比如 Redis 來做這種分散式鎖。但是一旦藉助了第三方儲存,我們就必須面對這個問題:Unlock是否能保證一定執行呢?
這個問題,我們面對的除了程式的bug之外,還有網路的不穩定,程序被殺死,伺服器被down機等。我們是無法保證Unlock一定被執行的。
那麼我們就一般在Lock的時候為這個鎖加一個超時時間作為兜底。
LockByExpire(duration);
DoJob();
Unlock();
這個超時時間是為了一旦出現異常情況導致Unlock沒有被執行,這個鎖在duration時間內也會被自動釋放。這個在redis中我們一般就是使用set ex
來進行鎖超時的設定。
但是有這個超時時間我們又遇上了問題,超時時間設定多久合適呢?當然要設定的比 DoJob 消耗的時間更長,否則的話,在任務還沒結束的時候,鎖就被釋放了,還是有可能導致並行任務的存在。
但是實際上,同樣由於網路超時問題,系統執行狀況問題等,我們是無法準確知道DoJob這個函數要執行多久的。那麼這時候怎麼辦呢?
有兩個辦法:
第一個方法,我們可以對DoJob做一個超時設定。讓DoJob最多隻能執行n秒,那麼我的分散式鎖的超時時長設定比n秒長就可以了。為一個任務設定超時時間在很多語言是可以做到的。比如golang 中的 TimeoutContext。
而第二種方法,就是我們先為鎖設定一個比較小的超時時長,然後不斷續期這個鎖。對一個鎖的不斷需求,也可以理解為重新開始加鎖,這種可以不斷續期的鎖,就叫做可重入鎖。
除了主執行緒之外,可重入鎖必然有一個另外的執行緒(或者攜程)可以對這個鎖進行續期,我們叫這個額外的程式叫做watchDog(看門狗)。
在Golang中,語言級別天生支援協程,所以這種可重入鎖就非常容易實現:
// DistributeLockRedis 基於redis的分散式可重入鎖,自動續租 type DistributeLockRedis struct { key string // 鎖的key expire int64 // 鎖超時時間 status bool // 上鎖成功標識 cancelFun context.CancelFunc // 用於取消自動續租攜程 redis redis.Client // redis控制程式碼 } // 建立可 func NewDistributeLockRedis(key string, expire int64) *DistributeLockRedis { return &DistributeLockRedis{ key : key, expire : expire, } } // TryLock 上鎖 func (dl *DistributeLockRedis) TryLock() (err error) { if err = dl.lock(); err != nil { return err } ctx, cancelFun := context.WithCancel(context.Background()) dl.cancelFun = cancelFun dl.startWatchDog(ctx) // 建立守護協程,自動對鎖進行續期 dl.status = true return nil } // competition 競爭鎖 func (dl *DistributeLockRedis) lock() error { if res, err := redis.String(dl.redis.Do(context.Background(), "SET", dl.key, 1, "NX", "EX", dl.expire)); err != nil { return err } return nil } // guard 建立守護協程,自動續期 func (dl *DistributeLockRedis) startWatchDog(ctx context.Context) { safeGo(func() error { for { select { // Unlock通知結束 case <-ctx.Done(): return nil default: // 否則只要開始了,就自動重入(續租鎖) if dl.status { if res, err := redis.Int(dl.redis.Do(context.Background(), "EXPIRE", dl.key, dl.expire)); err != nil { return nil } // 續租時間為 expire/2 秒 time.Sleep(time.Duration(dl.expire/2) * time.Second) } } } }) } // Unlock 釋放鎖 func (dl *DistributeLockRedis) Unlock() (err error) { // 這個重入鎖必須取消,放在第一個地方執行 if dl.cancelFun != nil { dl.cancelFun() // 釋放成功,取消重入鎖 } var res int if dl.status { if res, err = redis.Int(dl.redis.Do(context.Background(), "Del", dl.key)); err != nil { return fmt.Errorf("釋放鎖失敗") } if res == 1 { dl.status = false return nil } } return fmt.Errorf("釋放鎖失敗") }
這段程式碼的邏輯基本上都以註釋的形式來寫了。其中主要就在startWatchDog,對鎖進行重新續期
ctx, cancelFun := context.WithCancel(context.Background()) dl.cancelFun = cancelFun dl.startWatchDog(ctx) // 建立守護協程,自動對鎖進行續期 dl.status = true
首先建立一個cancelContext,它的context函數cancelFunc是給Unlock進行呼叫的。然後啟動一個goroutine程序來回圈續期。
這個新啟動的goroutine在主goroutine處理結束,呼叫Unlock的時候,才會結束,否則會在 過期時間/2 的時候,呼叫一次redis的expire命令來進行續期。
至於外部,在使用的時候如下
func Foo() error { key := foo // 建立可重入的分散式鎖 dl := NewDistributeLockRedis(key, 10) // 爭搶鎖 err := dl.TryLock() if err != nil { // 沒有搶到鎖 return err } // 搶到鎖的記得釋放鎖 defer func() { dl.Unlock() } // 做真正的任務 DoJob() }
以上就是Golang實現可重入鎖的範例程式碼的詳細內容,更多關於Golang可重入鎖的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45