Go語言并發(fā)編程臨界區(qū)的使用
臨界區(qū)是多線程/并發(fā)編程中的核心概念,指程序中訪問共享資源(如變量、數(shù)據(jù)結(jié)構(gòu)、文件等)的代碼段,這些資源在同一時間只能被一個線程訪問以避免數(shù)據(jù)競爭和不一致。
本篇文章著重介紹臨界區(qū),鎖的詳細(xì)介紹會在下一篇文章中。
基本定義
臨界區(qū)是指:
- 訪問共享資源的代碼片段
- 需要同步機(jī)制保護(hù)的部分
- 同一時間只允許一個執(zhí)行線程/goroutine進(jìn)入的區(qū)域
關(guān)鍵的特性:
- 共享資源訪問:涉及對共享內(nèi)存、文件、設(shè)備等資源的讀寫操作
- 原子性需求:臨界區(qū)內(nèi)的操作應(yīng)作為一個不可分割的單元執(zhí)行
- 互斥/排他訪問:必須確保同一時間只有一個執(zhí)行流能進(jìn)入臨界區(qū)
- 有限停留:線程應(yīng)盡快離開臨界區(qū),減少阻塞其他線程的時間
在Go中臨界區(qū)示例
無保護(hù)的臨界區(qū)(危險)
var counter int // 共享變量
func increment() {
counter++ // 這就是臨界區(qū)(沒有保護(hù))
}使用Mutex保護(hù)的臨界區(qū)
var (
counter int
mu sync.Mutex
)
func safeIncrement() {
mu.Lock() // 進(jìn)入臨界區(qū)前加鎖
defer mu.Unlock() // 確保退出時解鎖
counter++ // 受保護(hù)的臨界區(qū)
// 其他操作...
}臨界區(qū)與鎖的關(guān)系
| 概念 | 描述 |
|---|---|
| 臨界區(qū) | 需要保護(hù)的代碼段(概念) |
| 鎖 | 保護(hù)臨界區(qū)的實(shí)現(xiàn)機(jī)制(工具) |
| 關(guān)系 | 鎖用來劃定和保護(hù)臨界區(qū),臨界區(qū)是需要鎖保護(hù)的代碼范圍 |
臨界區(qū)的設(shè)計(jì)規(guī)范
1.最小化原則:
- 盡量減小臨界區(qū)的范圍
- 只包含必須同步的操作
// 不好:包含非必要操作 mu.Lock() data := fetchFromDatabase() // 耗時IO操作 sharedMap[key] = data mu.Unlock() // 更好:僅保護(hù)共享訪問 data := fetchFromDatabase() mu.Lock() sharedMap[key] = data mu.Unlock()
2.簡短執(zhí)行
- 避免在臨界區(qū)內(nèi)執(zhí)行耗時操作如:IO、復(fù)雜計(jì)算
- 典型臨界區(qū)應(yīng)能在微秒級完成
3.單一職責(zé)
- 一個臨界去最好只保護(hù)一個共享資源
- 避免多個不相關(guān)資源共同用同一個鎖
4.無嵌套原則
- 避免在臨界區(qū)內(nèi)調(diào)用可能獲取其他鎖的方法
- 防止死鎖發(fā)生
臨界區(qū)保護(hù)機(jī)制對比
1. 互斥鎖(Mutex)
var mu sync.Mutex
func accessShared() {
mu.Lock()
// 臨界區(qū)...
mu.Unlock()
}2. 讀寫鎖(RWMutex)
var rwMu sync.RWMutex
func readShared() {
rwMu.RLock()
// 只讀臨界區(qū)(允許多個讀者)
rwMu.RUnlock()
}
func writeShared() {
rwMu.Lock()
// 寫臨界區(qū)(獨(dú)占)
rwMu.Unlock()
}3. 通道(Channel)
var ch = make(chan struct{}, 1) // 容量1的通道模擬鎖
func accessShared() {
ch <- struct{}{} // 獲取"鎖"
// 臨界區(qū)...
<-ch // 釋放"鎖"
}常會遇到的問題
1. 數(shù)據(jù)競爭(Data Race)
// 兩個goroutine并發(fā)執(zhí)行此函數(shù)會導(dǎo)致數(shù)據(jù)競爭
func race() {
counter++ // 未保護(hù)的臨界區(qū)
}檢測:使用go run -race或go test -race
2. 死鎖(Deadlock)
func deadlock() {
mu.Lock()
mu.Lock() // 重復(fù)加鎖導(dǎo)致死鎖
mu.Unlock()
mu.Unlock()
}3. 活鎖(Livelock)
// 兩個goroutine不斷重試但無法進(jìn)展
func livelock() {
for {
if mu.TryLock() { // Go 1.18+
// 臨界區(qū)...
mu.Unlock()
break
}
time.Sleep(time.Millisecond) // 可能導(dǎo)致活鎖
}
}項(xiàng)目案例改編
銀行賬戶轉(zhuǎn)賬
type Account struct {
mu sync.Mutex
balance int
}
func (a *Account) Transfer(to *Account, amount int) error {
// 按固定順序加鎖防止死鎖
first, second := a, to
if a < to { // 通過地址比較確定順序
first, second = to, a
}
first.mu.Lock()
defer first.mu.Unlock()
second.mu.Lock()
defer second.mu.Unlock()
// 臨界區(qū)開始
if a.balance < amount {
return errors.New("insufficient balance")
}
a.balance -= amount
to.balance += amount
// 臨界區(qū)結(jié)束
return nil
}深度解讀:
- func (a *Account) Transfer(to *Account, amount int) error:定義了一個名為Transfer的方法,該方法屬于Account類型的接收者,表示從一個賬戶向另一個賬戶轉(zhuǎn)賬。方法接收兩個參數(shù),to是指向目標(biāo)賬戶的指針,amount是要轉(zhuǎn)賬的金額。返回值是error類型,用于處理可能出現(xiàn)的錯誤情況。
- 防止死鎖的機(jī)制:在進(jìn)行轉(zhuǎn)賬操作之前,首先對兩個賬戶進(jìn)行排序(通過比較兩個賬戶指針的內(nèi)存地址),確??偸窍孺i定地址較小的那個賬戶的互斥鎖,然后再鎖定地址較大的那個賬戶的互斥鎖。這種按固定順序加鎖的策略可以有效避免兩個或多個 goroutine 同時嘗試鎖定不同賬戶的互斥鎖時出現(xiàn)的死鎖情況。
- 互斥鎖的使用:通過first.mu.Lock()和second.mu.Lock()分別鎖定兩個賬戶的互斥鎖,確保在同一時間只有一個 goroutine 可以訪問這兩個賬戶的余額。defer first.mu.Unlock()和defer second.mu.Unlock()語句用于確保在函數(shù)執(zhí)行完畢后,無論是否發(fā)生錯誤,最終都能釋放這兩個賬戶的互斥鎖。
- 臨界區(qū):在兩個賬戶的互斥鎖都被成功鎖定之后,就開始執(zhí)行轉(zhuǎn)賬操作的臨界區(qū)代碼。首先檢查轉(zhuǎn)出賬戶的余額是否足夠覆蓋轉(zhuǎn)賬金額,如果余額不足,則返回一個錯誤信息"insufficient balance"。否則,從轉(zhuǎn)出賬戶扣除相應(yīng)的金額,并將該金額加到轉(zhuǎn)入賬戶的余額中
到此這篇關(guān)于Go語言并發(fā)編程臨界區(qū)的使用的文章就介紹到這了,更多相關(guān)Go語言 臨界區(qū)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
GO Cobra Termui庫開發(fā)終端命令行小工具輕松上手
這篇文章主要為大家介紹了GO語言開發(fā)終端命令行小工具,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
基于HLS創(chuàng)建Golang視頻流服務(wù)器的優(yōu)缺點(diǎn)
HLS 是 HTTP Live Streaming 的縮寫,是蘋果開發(fā)的一種基于 HTTP 的自適應(yīng)比特率流媒體傳輸協(xié)議。這篇文章主要介紹了基于 HLS 創(chuàng)建 Golang 視頻流服務(wù)器,需要的朋友可以參考下2021-08-08
Go語言學(xué)習(xí)之new函數(shù)的用法詳解
這篇文章主要為大家詳細(xì)介紹了Go語言中new()函數(shù)的相關(guān)知識以及具體用法,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以了解一下2023-05-05
go程序測試CPU占用率統(tǒng)計(jì)ps?vs?top兩種不同方式對比
這篇文章主要為大家介紹了go程序測試CPU占用率統(tǒng)計(jì)ps?vs?top兩種不同方式對比,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
Golang仿ps實(shí)現(xiàn)獲取Linux進(jìn)程信息
這篇文章主要為大家學(xué)習(xí)介紹了Golang如何仿ps實(shí)現(xiàn)獲取Linux進(jìn)程信息,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-07-07
Golang?IOT中的數(shù)據(jù)序列化與解析過程
這篇文章主要介紹了Golang?IOT中的數(shù)據(jù)序列化與解析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05
Go實(shí)現(xiàn)socks5服務(wù)器的方法
SOCKS5 是一個代理協(xié)議,它在使用TCP/IP協(xié)議通訊的前端機(jī)器和服務(wù)器機(jī)器之間扮演一個中介角色,使得內(nèi)部網(wǎng)中的前端機(jī)器變得能夠訪問Internet網(wǎng)中的服務(wù)器,或者使通訊更加安全,這篇文章主要介紹了Go實(shí)現(xiàn)socks5服務(wù)器的方法,需要的朋友可以參考下2023-07-07
Goland IDEA項(xiàng)目多開設(shè)置方式
這篇文章主要介紹了Goland IDEA項(xiàng)目多開設(shè)置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12

