網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
golang中的鎖分為互斥鎖、讀寫鎖、原子鎖即原子操作。
在 Golang 里有專門的方法來(lái)實(shí)現(xiàn)鎖,就是 sync 包,這個(gè)包有兩個(gè)很重要的鎖類型。一個(gè)叫 Mutex, 利用它可以實(shí)現(xiàn)互斥鎖。
一個(gè)叫 RWMutex,利用它可以實(shí)現(xiàn)讀寫鎖。
- 全局鎖 sync.Mutex,是同一時(shí)刻某一資源只能上一個(gè)鎖,此鎖具有排他性,上鎖后只能被此線程使用,直至解鎖。加鎖后即不能讀也不能寫。全局鎖是互斥鎖,即 sync.Mutex 是個(gè)互斥鎖。
- 讀寫鎖 sync.RWMutex ,將使用者分為讀者和寫者兩個(gè)概念,支持同時(shí)多個(gè)讀者一起讀共享資源,但寫時(shí)只能有一個(gè),并且在寫時(shí)不可以讀。理論上來(lái)說(shuō),sync.RWMutex 的 Lock() 也是個(gè)互斥鎖。
踩坑點(diǎn)
將上面的結(jié)論展開(kāi)一下,更清晰得說(shuō)(為避免理解偏差寧可嘮叨一些):
- sync.Mutex 的鎖是不可以嵌套使用的。
- sync.RWMutex 的 mu.Lock() 是不可以嵌套的。
- sync.RWMutex 的 mu.Lock() 中不可以嵌套 mu.RLock()。(這是個(gè)注意的地方)
否則,會(huì) panic fatal error: all goroutines are asleep - deadlock!
var l sync.RWMutex ? func lockAndRead() { // 可讀鎖內(nèi)使用可讀鎖 ?? ?l.RLock() ?? ?defer l.RUnlock() ? ?? ?l.RLock() ?? ?defer l.RUnlock() } ? func main() { ?? ?lockAndRead() ?? ?time.Sleep(5 * time.Second) }
而將 lockAndRead 換為以下三種函數(shù)均會(huì)造成 panic:
func lockAndRead1() { // 全局鎖內(nèi)使用全局鎖 ?? ?l.Lock() ?? ?defer l.Unlock() ? ?? ?l.Lock() ?? ?defer l.Unlock() } ? func lockAndRead2() { // 全局鎖內(nèi)使用可讀鎖 ?? ?l.Lock() ?? ?defer l.Unlock() // 由于 defer 是棧式執(zhí)行,所以這兩個(gè)鎖是嵌套結(jié)構(gòu) ? ?? ?l.RLock() ?? ?defer l.RUnlock() } ? func lockAndRead3() { // 可讀鎖內(nèi)使用全局鎖 ?? ?l.RLock() ?? ?defer l.RUnlock() ? ?? ?l.Lock() ?? ?defer l.Unlock() }
互斥鎖 Mutex
互斥鎖有兩個(gè)方法:加鎖、解鎖。
一個(gè)互斥鎖只能同時(shí)被一個(gè) goroutine 鎖定,其它 goroutine 將阻塞直到互斥鎖被解鎖(重新?tīng)?zhēng)搶對(duì)互斥鎖的鎖定)。
使用Lock加鎖后,不能再進(jìn)行加鎖,只有當(dāng)對(duì)其進(jìn)行Unlock解鎖之后,才能對(duì)其加鎖。這個(gè)很好理解。
- 如果對(duì)一個(gè)未加鎖的資源進(jìn)行解鎖,會(huì)引發(fā)panic異常。
- 可以在一個(gè)goroutine中對(duì)一個(gè)資源加鎖,而在另外一個(gè)goroutine中對(duì)該資源進(jìn)行解鎖。
- 不要在持有鎖的時(shí)候做 IO 操作。盡量只通過(guò)持有鎖來(lái)保護(hù) IO 操作需要的資源而不是 IO 操作本身
func (m *Mutex) Lock() func (m *Mutex) Unlock()
讀寫鎖 RWMutex
讀寫鎖有四個(gè)方法:讀的加鎖、解鎖,寫的加鎖、解鎖。
func ?(*RWMutex)Lock() func (*RWMutex)Unlock()
和
func (*RWMutex)RLock() func (*RWMutex)RUnlock()
RWMutex的使用主要事項(xiàng)
- 1、讀鎖的時(shí)候無(wú)需等待讀鎖的結(jié)束
- 2、讀鎖的時(shí)候要等待寫鎖的結(jié)束
- 3、寫鎖的時(shí)候要等待讀鎖的結(jié)束
- 4、寫鎖的時(shí)候要等待寫鎖的結(jié)束
謹(jǐn)防鎖拷貝
type MyMutex struct { ?? ?count int ?? ?sync.Mutex } ? func main() { ?? ?var mu MyMutex ?? ?mu.Lock() ?? ?var mu1 = mu ?? ?mu.count++ ?? ?mu.Unlock() ?? ?mu1.Lock() ?? ?mu1.count++ ?? ?mu1.Unlock() ?? ?fmt.Println(mu.count, mu1.count) }
加鎖后復(fù)制變量,會(huì)將鎖的狀態(tài)也復(fù)制,所以 mu1 其實(shí)是已經(jīng)加鎖狀態(tài),再加鎖會(huì)死鎖
查看數(shù)據(jù)競(jìng)爭(zhēng)
加上 -race 參數(shù)驗(yàn)證數(shù)據(jù)競(jìng)爭(zhēng)
以下代碼有什么問(wèn)題,怎么解決?
func main() { ?? ?total, sum := 0, 0 ?? ?for i := 1; i <= 10; i++ { ?? ??? ?sum += i ?? ??? ?go func() { ?? ??? ??? ?total += i ?? ??? ?}() ?? ?} ?? ?fmt.Printf("total:%d sum %d", total, sum) }
該題的第二個(gè)考點(diǎn):data race。
因?yàn)榇嬖诙?goroutine 同時(shí)寫 total 變量的問(wèn)題,所以有數(shù)據(jù)競(jìng)爭(zhēng)。
可以加上 -race 參數(shù)驗(yàn)證
go run -race main.go ================== WARNING: DATA RACE Read at 0x00c0001b4020 by goroutine 8: ? main.main.func1() ? ? ? /Users/xuxinhua/main.go:12 +0x57 ? Previous write at 0x00c0001b4020 by main goroutine: ? main.main() ? ? ? /Users/xuxinhua/main.go:9 +0x10b ? Goroutine 8 (running) created at: ? main.main() ? ? ? /Users/xuxinhua/main.go:11 +0xe7 ==================
正確答案
package main ? import ( ? ? "sync/atomic" ? ? "sync" ? ? "fmt" ) ? func main() { ? ? var wg sync.WaitGroup ? ? var total int64 ? ? sum := 0 ? ? for i := 1; i <= 10; i++ { ? ? ? ? wg.Add(1) ? ? ? ? sum += i ? ? ? ? go func(i int) { ? ? ? ? ? ? defer wg.Done() ? ? ? ? ? ? atomic.AddInt64(&total, int64(i)) ? ? ? ? }(i) ? ? } ? ? wg.Wait() ? ? ? fmt.Printf("total:%d sum %d", total, sum) }
總結(jié)
原文鏈接:https://blog.csdn.net/hudeyong926/article/details/126467118
相關(guān)推薦
- 2023-01-13 分布式緩存Redis與Memcached的優(yōu)缺點(diǎn)區(qū)別比較_數(shù)據(jù)庫(kù)其它
- 2022-09-19 C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之單鏈表存儲(chǔ)詳解_C 語(yǔ)言
- 2022-06-17 go語(yǔ)言beego框架分頁(yè)器操作及接口頻率限制示例_Golang
- 2022-11-07 如何使用python生成大量數(shù)據(jù)寫入es數(shù)據(jù)庫(kù)并查詢操作_python
- 2022-09-03 如何通過(guò)memberlist庫(kù)實(shí)現(xiàn)gossip管理集群及集群數(shù)據(jù)交互問(wèn)題_相關(guān)技巧
- 2022-09-20 Python?flask使用ajax上傳文件的示例代碼_python
- 2022-06-12 詳解Go語(yǔ)言中的數(shù)據(jù)類型及類型轉(zhuǎn)換_Golang
- 2022-08-12 Python中深拷貝與淺拷貝的區(qū)別介紹_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支