網(wǎng)站首頁 編程語言 正文
什么是并發(fā)安全?
在高并發(fā)場景下,進(jìn)程、線程(協(xié)程)可能會(huì)發(fā)生資源競爭,導(dǎo)致數(shù)據(jù)臟讀、臟寫、死鎖等問題,為了避免此類問題的發(fā)生,就有了并發(fā)安全。
這里舉一個(gè)簡單的例子:
var data int go func() { data++ }() if data == 0 { fmt.Printf("the value is %v.\n", data) }
在這段代碼中
第2行g(shù)o關(guān)鍵字開啟了一個(gè)新的協(xié)程,來執(zhí)行data++操作
第5行,對data變量進(jìn)行了讀取判斷的操作
以上兩部是由2個(gè)不同線程/協(xié)程運(yùn)行,且沒有任何措施保證執(zhí)行順序,所以執(zhí)行結(jié)果是不確定的。
- 沒有輸出。(第3行是在第5行之前執(zhí)行的)
- 輸出 the value is 0。(第5行和第6行在第3行之前執(zhí)行)
- 輸出 the value is 1。(第5行在第3行之前執(zhí)行,但第3行在第6行之前執(zhí)行)
Go如何保證并發(fā)安全
目前了解到的,大概有這3種,Mutex、Channel、Atomic
Mutex
加鎖應(yīng)該是最常見的并發(fā)控制方法,一般分成兩種,樂觀鎖和悲觀鎖。
鎖是由操作系統(tǒng)的調(diào)度器來實(shí)現(xiàn)的,鎖通常用來保護(hù)一段邏輯,
悲觀鎖
悲觀鎖是一種悲觀思想,它總認(rèn)為最壞的情況可能會(huì)出現(xiàn)。不管意料之外的結(jié)果是否會(huì)發(fā)生,只要存在發(fā)生的可能,就在操作這個(gè)資源之前先上鎖。例如互斥鎖、讀寫鎖都是悲觀鎖。
在go中,除了automic,其它都是悲觀鎖
悲觀鎖應(yīng)該都是由操作系統(tǒng)的調(diào)度器來實(shí)現(xiàn)的,通常用來保護(hù)一段邏輯,主要是通過阻塞其它線程,保證當(dāng)前時(shí)刻只有一個(gè)線程在對資源進(jìn)行操作,因此性能相對較差,浪費(fèi)了計(jì)算機(jī)多核的優(yōu)勢。
樂觀鎖
樂觀鎖的思想與悲觀鎖的思想相反,它總認(rèn)為資源和數(shù)據(jù)不會(huì)被別人所修改,所以讀取不會(huì)上鎖,但是樂觀鎖在進(jìn)行寫入操作的時(shí)候會(huì)判斷當(dāng)前數(shù)據(jù)是否被修改過。
樂觀鎖的實(shí)現(xiàn)方案主要包含CAS和版本號機(jī)制。
樂觀鎖適用于多讀的場景,可以提高吞吐量。
版本號機(jī)制
通過在數(shù)據(jù)表中,增加一個(gè)版本號字段,當(dāng)數(shù)據(jù)發(fā)生更新時(shí),版本號值發(fā)生改變。 例如一個(gè)線程A想要更新變量s的值,在讀取s的值的同時(shí)讀取版本號,在提交更新時(shí),用之前讀到的版本號值與當(dāng)前的版本號值進(jìn)行比對,當(dāng)且僅當(dāng)版本號值一致時(shí),才會(huì)觸發(fā)更新,否則不斷進(jìn)行重試,直到更新成功。
CAS
CAS全名為Compare And Swap,即比較與轉(zhuǎn)換,是一種有名的無鎖算法。在不使用鎖的情況下,實(shí)現(xiàn)多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實(shí)現(xiàn)變量的同步,所以也叫非阻塞同步,
互斥鎖
GO使用Sync包的Mutex類型來實(shí)現(xiàn)互斥鎖,它能保證同時(shí)只有一個(gè)goroutine可以訪問資源。
func sample() { var l sync.Mutex l.Lock() defer l.Unlock() // so something }
讀寫互斥鎖
GO使用Sync包的RWMutex類型來實(shí)現(xiàn)互斥鎖。當(dāng)我們?nèi)ゲl(fā)的讀取一個(gè)資源,只要數(shù)據(jù)沒有發(fā)生寫入,是沒必要加鎖的。因此讀多寫少的情況下,使用讀寫互斥鎖是更好的選擇,性能更好。
讀寫鎖分為兩種:讀鎖和寫鎖。
當(dāng)一個(gè)goroutine獲取讀鎖之后,其他的goroutine如果是獲取讀鎖可以順利獲得,如果是獲取寫鎖就會(huì)等待;
當(dāng)一個(gè)goroutine獲取寫鎖之后,其他的goroutine無論是獲取讀鎖還是寫鎖都會(huì)等待。
package main import ( "fmt" "sync" "time" ) var ( wg sync.WaitGroup rwlock sync.RWMutex ) func write() { rwlock.Lock() // 加寫鎖 time.Sleep(10 * time.Millisecond) rwlock.Unlock() // 解寫鎖 wg.Done() } func read() { rwlock.RLock() // 加讀鎖 time.Sleep(time.Millisecond) rwlock.RUnlock() // 解讀鎖 wg.Done() } func main() { start := time.Now() //讀多 for i := 0; i < 1000; i++ { wg.Add(1) go read() } //寫少 for i := 0; i < 10; i++ { wg.Add(1) go write() } wg.Wait() end := time.Now() fmt.Println(end.Sub(start)) }
原文鏈接:https://juejin.cn/post/7025797208209358862
相關(guān)推薦
- 2022-09-17 Redis請求處理的流程分析_Redis
- 2022-10-12 詳解C++異常處理機(jī)制示例介紹_C 語言
- 2022-01-21 【每天一個(gè) Linux 命令】Linux命令 mkdir,cat,touch,vi/vim
- 2022-05-22 C++設(shè)計(jì)模式中的橋模式你了解嗎_C 語言
- 2022-10-10 C++淺析程序中內(nèi)存的分布_C 語言
- 2022-08-15 Python類的定義繼承調(diào)用比較方法技巧_python
- 2022-12-15 C++?Boost?Lambda表達(dá)式詳解_C 語言
- 2022-12-07 org.apache.tomcat.util.http.fileupload.IOUtils報(bào)錯(cuò)對應(yīng)
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- 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)證過濾器
- 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)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支