日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

GO語言并發(fā)之好用的sync包詳解_Golang

作者:機(jī)智的程序員小熊 ? 更新時(shí)間: 2023-02-04 編程語言

sync.Map 并發(fā)安全的Map

反例如下,兩個(gè)Goroutine分別讀寫。

func unsafeMap(){
	var wg sync.WaitGroup
	m := make(map[int]int)
	wg.Add(2)
	go func() {
		defer wg.Done()
		for i := 0; i < 10000; i++ {
			m[i] = i
		}
	}()

	go func() {
		defer wg.Done()
		for i := 0; i < 10000; i++ {
			fmt.Println(m[i])
		}
	}()
	wg.Wait()
}

執(zhí)行報(bào)錯(cuò):

0
fatal error: concurrent map read and map write

goroutine 7 [running]:
runtime.throw({0x10a76fa, 0x0})
......

使用并發(fā)安全的Map

func safeMap() {
	var wg sync.WaitGroup
	var m sync.Map
	wg.Add(2)
	go func() {
		defer wg.Done()
		for i := 0; i < 10000; i++ {
			m.Store(i, i)
		}
	}()

	go func() {
		defer wg.Done()
		for i := 0; i < 10000; i++ {
			fmt.Println(m.Load(i))
		}
	}()
	wg.Wait()
}
  • 不需要make就能使用
  • 還內(nèi)置了StoreLoadLoadOrStoreDeleteRange等操作方法,自行體驗(yàn)。

sync.Once 只執(zhí)行一次

很多場景下我們需要確保某些操作在高并發(fā)的場景下只執(zhí)行一次,例如只加載一次配置文件、只關(guān)閉一次通道等。

init 函數(shù)是當(dāng)所在的 package 首次被加載時(shí)執(zhí)行,若遲遲未被使用,則既浪費(fèi)了內(nèi)存,又延長了程序加載時(shí)間。

sync.Once 可以在代碼的任意位置初始化和調(diào)用,因此可以延遲到使用時(shí)再執(zhí)行,并發(fā)場景下是線程安全的。

在多數(shù)情況下,sync.Once 被用于控制變量的初始化,這個(gè)變量的讀寫滿足如下三個(gè)條件:

  • 當(dāng)且僅當(dāng)?shù)谝淮卧L問某個(gè)變量時(shí),進(jìn)行初始化(寫);
  • 變量初始化過程中,所有讀都被阻塞,直到初始化完成;
  • 變量僅初始化一次,初始化完成后駐留在內(nèi)存里。
var loadOnce sync.Once
var x int
for i:=0;i<10;i++{
    loadOnce.Do(func() {
        x++
    })
}
fmt.Println(x)

輸出

1

sync.Cond 條件變量控制

sync.Cond 基于互斥鎖/讀寫鎖,它和互斥鎖的區(qū)別是什么呢?

互斥鎖 sync.Mutex 通常用來保護(hù)臨界區(qū)和共享資源,條件變量 sync.Cond 用來協(xié)調(diào)想要訪問共享資源的 goroutine

也就是在存在共享變量時(shí),可以直接使用sync.Cond來協(xié)調(diào)共享變量,比如最常見的共享隊(duì)列,多消費(fèi)多生產(chǎn)的模式。

我一開始也很疑惑為什么不使用channelselect的模式來做生產(chǎn)者消費(fèi)者模型(實(shí)際上也可以),這一節(jié)不是重點(diǎn)就不展開討論了。

創(chuàng)建實(shí)例

func NewCond(l Locker) *Cond

NewCond 創(chuàng)建 Cond 實(shí)例時(shí),需要關(guān)聯(lián)一個(gè)鎖。

廣播喚醒所有

func (c *Cond) Broadcast()

Broadcast 喚醒所有等待條件變量 cgoroutine,無需鎖保護(hù)。

喚醒一個(gè)協(xié)程

func (c *Cond) Signal()

Signal 只喚醒任意 1 個(gè)等待條件變量 cgoroutine,無需鎖保護(hù)。

等待

func (c *Cond) Wait()

每個(gè) Cond 實(shí)例都會(huì)關(guān)聯(lián)一個(gè)鎖 L(互斥鎖 *Mutex,或讀寫鎖 *RWMutex),當(dāng)修改條件或者調(diào)用 Wait 方法時(shí),必須加鎖。

舉個(gè)不恰當(dāng)?shù)睦樱瑢?shí)現(xiàn)一個(gè)經(jīng)典的生產(chǎn)者和消費(fèi)者模式,但有先決條件:

  • 邊生產(chǎn)邊消費(fèi),可以多生產(chǎn)多消費(fèi)。
  • 生產(chǎn)后通知消費(fèi)。
  • 隊(duì)列為空時(shí),暫停等待。
  • 支持關(guān)閉,關(guān)閉后等待消費(fèi)結(jié)束。
  • 關(guān)閉后依然可以生產(chǎn),但無法消費(fèi)了。
var (
	cnt          int
	shuttingDown = false
	cond         = sync.NewCond(&sync.Mutex{})
)
  • cnt 為隊(duì)列,這里直接用變量代替了,變量就是隊(duì)列長度。
  • shuttingDown 消費(fèi)關(guān)閉狀態(tài)。
  • cond 現(xiàn)成的隊(duì)列控制。

生產(chǎn)者

func Add(entry int) {
	cond.L.Lock()
	defer cond.L.Unlock()
	cnt += entry
	fmt.Println("生產(chǎn)咯,來消費(fèi)吧")
	cond.Signal()
}

消費(fèi)者

func Get() (int, bool) {
	cond.L.Lock()
	defer cond.L.Unlock()
	for cnt == 0 && !shuttingDown {
		fmt.Println("未關(guān)閉但空了,等待生產(chǎn)")
		cond.Wait()
	}
	if cnt == 0 {
		fmt.Println("關(guān)閉咯,也消費(fèi)完咯")
		return 0, true
	}
	cnt--
	return 1, false
}

關(guān)閉程序

func Shutdown() {
	cond.L.Lock()
	defer cond.L.Unlock()
	shuttingDown = true
	fmt.Println("要關(guān)閉咯,大家快消費(fèi)")
	cond.Broadcast()
}

主程序

var wg sync.WaitGroup
	wg.Add(2)
	time.Sleep(time.Second)
	go func() {
		defer wg.Done()
		for i := 0; i < 10; i++ {
			go Add(1)
			if i%5 == 0 {
				time.Sleep(time.Second)
			}
		}
	}()
	go func() {
		defer wg.Done()
		shuttingDown := false
		for !shuttingDown {
			var cur int
			cur, shuttingDown = Get()
			fmt.Printf("當(dāng)前消費(fèi) %d, 隊(duì)列剩余 %d \n", cur, cnt)
		}
	}()
	time.Sleep(time.Second * 5)
	Shutdown()
	wg.Wait()
  • 分別創(chuàng)建生產(chǎn)者與消費(fèi)者。
  • 生產(chǎn)10個(gè),每5個(gè)休息1秒。
  • 持續(xù)消費(fèi)。
  • 主程序關(guān)閉隊(duì)列。

輸出

生產(chǎn)咯,來消費(fèi)吧
當(dāng)前消費(fèi) 1, 隊(duì)列剩余 0?
未關(guān)閉但空了,等待生產(chǎn)
生產(chǎn)咯,來消費(fèi)吧
生產(chǎn)咯,來消費(fèi)吧
當(dāng)前消費(fèi) 1, 隊(duì)列剩余 1?
當(dāng)前消費(fèi) 1, 隊(duì)列剩余 0?
未關(guān)閉但空了,等待生產(chǎn)
生產(chǎn)咯,來消費(fèi)吧
生產(chǎn)咯,來消費(fèi)吧
生產(chǎn)咯,來消費(fèi)吧
當(dāng)前消費(fèi) 1, 隊(duì)列剩余 2?
當(dāng)前消費(fèi) 1, 隊(duì)列剩余 1?
當(dāng)前消費(fèi) 1, 隊(duì)列剩余 0?
未關(guān)閉但空了,等待生產(chǎn)
生產(chǎn)咯,來消費(fèi)吧
生產(chǎn)咯,來消費(fèi)吧
生產(chǎn)咯,來消費(fèi)吧
生產(chǎn)咯,來消費(fèi)吧
當(dāng)前消費(fèi) 1, 隊(duì)列剩余 1?
當(dāng)前消費(fèi) 1, 隊(duì)列剩余 2?
當(dāng)前消費(fèi) 1, 隊(duì)列剩余 1?
當(dāng)前消費(fèi) 1, 隊(duì)列剩余 0?
未關(guān)閉但空了,等待生產(chǎn)
要關(guān)閉咯,大家快消費(fèi)
關(guān)閉咯,也消費(fèi)完咯
當(dāng)前消費(fèi) 0, 隊(duì)列剩余 0

小結(jié)

1.sync.Map 并發(fā)安全的Map。

2.sync.Once 只執(zhí)行一次,適用于配置讀取、通道關(guān)閉。

3.sync.Cond 控制協(xié)調(diào)共享資源。

原文鏈接:https://juejin.cn/post/7182059760567451685

欄目分類
最近更新