Go語(yǔ)言sync.Once和sync.Cond的實(shí)現(xiàn)
一.sync.Once
Once(單次執(zhí)行)
用途:確保某個(gè)操作只執(zhí)行一次(如初始化配置)
核心方法:Do(f func()):保證 f只執(zhí)行一次
package main
import (
"fmt"
"sync"
)
var (
config map[string]string
once sync.Once
wg sync.WaitGroup
)
func loadConfig() {
once.Do(func() {
fmt.Println("Loading config...")
config = map[string]string{"key": "value"}
})
}
func main() {
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done() // 確保 goroutine 結(jié)束時(shí)減少計(jì)數(shù)器
loadConfig()
}()
}
wg.Wait() // 等待所有 goroutine 完成
}
可以確保這個(gè)協(xié)程只進(jìn)行一次
二.sync.Cond
sync.Cond 是 golang 標(biāo)準(zhǔn)庫(kù)提供的并發(fā)協(xié)調(diào)器,用于支援開(kāi)放人員在指定條件下阻塞和喚醒協(xié)程的操作.
Wait():釋放鎖并阻塞,直到被喚醒。Signal():?jiǎn)拘岩粋€(gè)等待的 goroutine。Broadcast():?jiǎn)拘阉械却?goroutine。

2.1 數(shù)據(jù)結(jié)構(gòu)與構(gòu)造器方法
type Cond struct {
// 不可以對(duì)其進(jìn)行值拷貝
noCopy noCopy
// 一個(gè)自旋鎖
L Locker
// 一個(gè)隊(duì)列,存放阻塞的goroutine
notify notifyList
checker copyChecker
}
// NewCond returns a new Cond with Locker l.
func NewCond(l Locker) *Cond {
return &Cond{L:l}
}(1)成員變量 noCopy + checker 是一套組合拳,保證 Cond 在第一次使用后不允許被復(fù)制;
(2)核心變量 L,一把鎖,用于實(shí)現(xiàn)阻塞操作;
(3)核心變量 notify,阻塞鏈表,分別存儲(chǔ)了調(diào)用 Cond.Wait() 方法的次數(shù)、goroutine 被喚醒的次數(shù)、一把系統(tǒng)運(yùn)行時(shí)的互斥鎖以及鏈表的頭尾節(jié)點(diǎn).
type notifyList struct {
wait uint32
notify uint32
lock uintptr // key field of the mutex
head unsafe.Pointer
tail unsafe.Pointer
}2.2 Cond.Wait
作用:把當(dāng)前這個(gè)持有鎖的goroutine,釋放鎖,陷入一個(gè)被動(dòng)阻塞的狀態(tài),加入阻塞隊(duì)列里面。
什么時(shí)候被喚醒呢?
當(dāng)有其他的goroutine也持有這個(gè)Cond的引用,使用Signal函數(shù)的時(shí)候,會(huì)首先喚醒隊(duì)首的goroutine
通過(guò)這個(gè)機(jī)制就可以實(shí)現(xiàn)異步goroutine的協(xié)調(diào),比如需要某一個(gè)goroutine實(shí)現(xiàn)了某一個(gè)動(dòng)作,另外一個(gè)goroutine才可以繼續(xù)執(zhí)行的一個(gè)場(chǎng)景。
使用的前置條件
看下面的代碼我們會(huì)發(fā)現(xiàn),它有一個(gè)解鎖的操作,所以在調(diào)用他之前,必須是加鎖的狀態(tài),只有這樣才可以執(zhí)行,然后陷入被動(dòng)阻塞的狀態(tài),阻塞喚醒之后,才會(huì)重新加鎖。
func (c *Cond) Wait() {
c.checker.check()
t := runtime_notifyListAdd(&c.notify)
c.L.Unlock()
runtime_notifyListWait(&c.notify, t)
c.L.Lock()
}(1)檢查 Cond 是否在使用過(guò)后被拷貝,是則 panic;
(2)該 Cond 阻塞鏈表 wait 統(tǒng)計(jì)數(shù)加 1;
(3)當(dāng)前協(xié)程釋放鎖,因?yàn)榻酉聛?lái)即將被 操作系統(tǒng) park;
(4)將當(dāng)前協(xié)程包裝成節(jié)點(diǎn),添加到 Cond 的阻塞隊(duì)列當(dāng)中,并調(diào)用 park 操作將當(dāng)前協(xié)程掛起;
(5)協(xié)程被喚醒后,重新嘗試獲取鎖.
2.3 Cond.Signal
作用:就是喚醒隊(duì)首的goroutine喚醒
func (c *Cond) Signal() {
c.checker.check()
runtime_notifyListNotifyOne(&c.notify)
}(1)檢查 Cond 是否在首次使用后被拷貝,是則 panic;
(2)該 Cond 阻塞鏈表 notify 統(tǒng)計(jì)數(shù)加 1;
(3)從頭開(kāi)始遍歷阻塞鏈表,喚醒一個(gè)等待時(shí)間最長(zhǎng)的 goroutine.
2.4 Cond.BroadCast
作用:就是將阻塞隊(duì)列里面的所有g(shù)oroutine都進(jìn)行喚醒。
func (c *Cond) Broadcast() {
c.checker.check()
runtime_notifyListNotifyAll(&c.notify)
}(1)檢查 Cond 是否在首次使用后被拷貝,是則 panic;
(2)取 wait 值賦值給 notify;
(3)喚醒阻塞鏈表所有節(jié)點(diǎn).
2.5 使用案例
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var mu sync.Mutex
cond := sync.NewCond(&mu)
// 共享狀態(tài)
isReady := false
// 等待條件的goroutine
go func() {
fmt.Println("等待者: 等待條件滿足...")
cond.L.Lock()
defer cond.L.Unlock()
// 使用循環(huán)防止虛假喚醒
for !isReady {
cond.Wait() // 釋放鎖并阻塞,喚醒時(shí)會(huì)重新獲得鎖
fmt.Println("等待者: 被喚醒,檢查條件")
}
fmt.Println("等待者: 條件已滿足!")
}()
// 改變條件的goroutine
go func() {
time.Sleep(2 * time.Second) // 模擬耗時(shí)操作
fmt.Println("觸發(fā)者: 準(zhǔn)備改變條件...")
cond.L.Lock()
isReady = true
cond.L.Unlock()
fmt.Println("觸發(fā)者: 發(fā)送通知")
cond.Signal() // 喚醒一個(gè)等待的goroutine
}()
time.Sleep(3 * time.Second) // 等待所有g(shù)oroutine完成
}到此這篇關(guān)于Go語(yǔ)言sync.Once和sync.Cond的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go語(yǔ)言sync.Once和sync.Cond內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang實(shí)現(xiàn)組合模式和裝飾模式實(shí)例詳解
這篇文章主要介紹了Golang實(shí)現(xiàn)組合模式和裝飾模式,本文介紹組合模式和裝飾模式,golang實(shí)現(xiàn)兩種模式有共同之處,但在具體應(yīng)用場(chǎng)景有差異。通過(guò)對(duì)比兩個(gè)模式,可以加深理解,需要的朋友可以參考下2022-11-11
Goland使用delve進(jìn)行遠(yuǎn)程調(diào)試的詳細(xì)教程
網(wǎng)上給出的使用delve進(jìn)行遠(yuǎn)程調(diào)試,都需要先在本地交叉編譯或者在遠(yuǎn)程主機(jī)上編譯出可運(yùn)行的程序,然后再用delve在遠(yuǎn)程啟動(dòng)程序,本教程會(huì)將上面的步驟簡(jiǎn)化為只需要兩步,1,在遠(yuǎn)程運(yùn)行程序2,在本地啟動(dòng)調(diào)試,需要的朋友可以參考下2024-08-08
go如何調(diào)用C動(dòng)態(tài)庫(kù)函數(shù)
這篇文章主要介紹了go如何調(diào)用C動(dòng)態(tài)庫(kù)函數(shù)的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-05-05
基于Go語(yǔ)言實(shí)現(xiàn)選擇排序算法及優(yōu)化
選擇排序是一種簡(jiǎn)單的比較排序算法.這篇文章將利用Go語(yǔ)言實(shí)現(xiàn)冒泡排序算法,文中的示例代碼講解詳細(xì),對(duì)學(xué)習(xí)Go語(yǔ)言有一定的幫助,需要的可以參考一下2022-12-12
golang中的select關(guān)鍵字用法總結(jié)
這篇文章主要介紹了golang中的select關(guān)鍵字用法總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06

