91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Go語言sync包中的鎖都在什么場景下用

馬哥Linux運(yùn)維 ? 來源:JWang的博客 ? 作者:JWang ? 2021-10-26 09:35 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

Go 語言 sync 包中的鎖都在什么場景下用?怎么用?本文對 sync 包內(nèi)的鎖做了梳理。

今天談一下鎖,以及 Go 里面 Sync 包里面自帶的各種鎖,說到鎖這個概念,在日常生活中,鎖是為了保護(hù)一些東西,比如門鎖、密碼箱鎖,可以理解對資源的保護(hù)。在編程里面,鎖也是為了保護(hù)資源,比如說對文件加鎖,同一時間只也許一個用戶修改,這種鎖一般叫作文件鎖。

實(shí)際開發(fā)中,鎖又可分為互斥鎖(排它鎖)、讀寫鎖、共享鎖、自旋鎖,甚至還有悲觀鎖、樂觀鎖這種說法。在 Mysql 數(shù)據(jù)庫里面鎖的應(yīng)用更多,比如行鎖、表鎖、間隙鎖,有點(diǎn)眼花繚亂。拋開這些概念,在編程領(lǐng)域,鎖的本質(zhì)是為了解決并發(fā)情況下對數(shù)據(jù)資源的訪問問題,如果我們不加鎖,并發(fā)讀寫一塊數(shù)據(jù)必然會產(chǎn)生問題,如果直接加個互斥鎖問題是解決了,但是會嚴(yán)重影響讀寫性能,所以后面又產(chǎn)生了更復(fù)雜的鎖機(jī)制,在數(shù)據(jù)安全性和性能之間找到最佳平衡點(diǎn)。

正常來說,只有在并發(fā)編程下才會需要鎖,比如說多個線程(在 Go 里面則是協(xié)程)同時讀寫一個文件,下面我以一個文件為例,來解釋這幾種鎖的概念:

如果我們使用互斥鎖,那么同一時間只能由一線程去操作(讀或?qū)懀?,這就是像是咱們?nèi)ド蠋?,一個坑位同一時間只能蹲一個人,這就是廁所門鎖的作用。

如果我們使用讀寫鎖,意味著可以同時有多個線程讀取這個文件,但是寫的時候不能讀,并且只能由一個線程去寫。這個鎖實(shí)際上是互斥鎖的改進(jìn)版,很多時候我們之所以給文件加鎖是為了避免你在寫的過程中有人讀到了臟數(shù)據(jù)。

如果我們使用共享鎖,根據(jù)我查到資料,這種叫法大多數(shù)是源自 MySQL 事務(wù)里面的鎖概念,它意味著只能讀數(shù)據(jù),并不能修改數(shù)據(jù)。

如果我們使用自旋鎖,則意味著當(dāng)一個線程在獲取鎖的時候,如果鎖已經(jīng)被其它線程獲取,那么該線程將循環(huán)等待,然后不斷的判斷鎖是否能夠被成功獲取,直到獲取到鎖才會退出循環(huán)。

這些鎖的機(jī)制在 Go 里面有什么應(yīng)用呢,下面大家一起看看 Go 標(biāo)準(zhǔn)庫里面 sync 包提供的一些非常強(qiáng)大的基于鎖的實(shí)現(xiàn)。

1. 文件鎖

文件鎖和 sync 包沒關(guān)系,這里面只是順便說一下,舉個例子,磁盤上面有一個文件,必須保證同一時間只能由一個人打開,這里的同一時間是指操作系統(tǒng)層面的,并不是指應(yīng)用層面,文件鎖依賴于操作系統(tǒng)實(shí)現(xiàn)。

在 C 或 PHP 里面,文件鎖會使用一個 flock 的函數(shù)去實(shí)現(xiàn),其實(shí) Go 里面也類似:

funcmain(){
varf="/var/logs/app.log"
file,err:=os.OpenFile(f,os.O_RDWR,os.ModeExclusive)
iferr!=nil{
panic(err)
}
deferfile.Close()

//調(diào)用系統(tǒng)調(diào)用加鎖
err=syscall.Flock(int(file.Fd()),syscall.LOCK_EX|syscall.LOCK_NB)
iferr!=nil{
panic(err)
}
defersyscall.Flock(int(file.Fd()),syscall.LOCK_UN)
//讀取文件內(nèi)容
all,err:=ioutil.ReadAll(file)
iferr!=nil{
panic(err)
}

fmt.Printf("%s",all)
time.Sleep(time.Second*10)//模擬耗時操作
}

需要說明一下,F(xiàn)lock 函數(shù)第一個參數(shù)是文件描述符,第二個參數(shù)是鎖的類型,分為 LOCK_EX(排它鎖)、LOCK_SH(讀共享鎖)、LOCK_NB(遭遇鎖的表現(xiàn),遇到排它鎖的時候默認(rèn)會被阻塞,NB 即非阻塞,直接返回 Error)、LOCK_UN(解鎖)。

如果這時候你打開另外一個終端再次運(yùn)行這個程序你會發(fā)現(xiàn)報錯信息如下:

panic:resourcetemporarilyunavailable

文件鎖保證了一個文件在操作系統(tǒng)層面的數(shù)據(jù)讀寫安全,不過實(shí)際應(yīng)用中并不常見,畢竟大部分時候我們都是使用數(shù)據(jù)庫去做數(shù)據(jù)存儲,極少使用文件。

2.sync.Mutex

下面我所說的這些鎖都是應(yīng)用級別的鎖,位于 Go 標(biāo)準(zhǔn)庫 sync 包里面,各有各的應(yīng)用場景。

這是一個標(biāo)準(zhǔn)的互斥鎖,平時用的也比較多,用法也非常簡單,lock 用于加鎖,unlock 用于解鎖,配合 defer 使用,完美。

為了更好的展示鎖的應(yīng)用,這個舉一個沒有實(shí)際意義的例子,給一個 int 變量做加法,用 2 個協(xié)程并發(fā)的去做加法。

variint

funcmain(){
goadd(&i)

time.Sleep(time.Second*3)

println(i)
}

funcadd(i*int){
forj:=0;j10000;j++{
*i=*i+1
}
}

我們想要得到的正常結(jié)果是 20000,然而實(shí)際上并不是,其結(jié)果是不固定的,很可能少于 20000,大家多運(yùn)行幾次便可得知。

假設(shè)你多加一行 runtime.GOMAXPROCS(1),你會發(fā)現(xiàn)結(jié)果一直是正確的,這是為什么呢?

用一個比較理論的說法,這是因?yàn)楫a(chǎn)生了數(shù)據(jù)競爭(data race)問題,在 Go 里面我們可以在 go run 后面加上-race來檢測數(shù)據(jù)競爭,結(jié)果會告訴你在哪一行產(chǎn)生的,非常實(shí)用。

gorun-racemain.go
==================
WARNING:DATARACE
Readat0x00000056ccb8bygoroutine7:
main.add()
main.go:23+0x43
Previouswriteat0x00000056ccb8bygoroutine6:
main.add()
main.go:23+0x59
Goroutine7(running)createdat:
main.main()
main.go:14+0x76
Goroutine6(running)createdat:
main.main()
main.go:13+0x52
==================
20000
Found1datarace(s)
exitstatus66

解決這個問題,有多種解法,我們當(dāng)然可以換個寫法,比如說用 chan 管道去做加法(chan 底層也用了鎖),實(shí)際上在 Go 里面更推薦去使用 chan 解決數(shù)據(jù)同步問題,而不是直接用鎖機(jī)制。

在上面的這個例子里面我們需要在 add 方法里面寫,每次操作之前 lock,然后 unlock:

funcadd(i*int){
forj:=0;j10000;j++{
s.Lock()
*i=*i+1
s.Unlock()
}
}

3.sync.RWMutex

讀寫鎖是互斥鎖的升級版,它最大的優(yōu)點(diǎn)就是支持多讀,但是讀和寫、以及寫與寫之間還是互斥的,所以比較適合讀多寫少的場景。

它的實(shí)現(xiàn)里面有 5 個方式:

func(rw*RWMutex)Lock()
func(rw*RWMutex)RLock()
func(rw*RWMutex)RLocker()Locker
func(rw*RWMutex)RUnlock()
func(rw*RWMutex)Unlock()

其中 Lock() 和 Unlock() 用于申請和釋放寫鎖,RLock() 和 RUnlock() 用于申請和釋放讀鎖,RLocker() 用于返回一個實(shí)現(xiàn)了 Lock() 和 Unlock() 方法的 Locker 接口

實(shí)話說,平時這個用的真不多,主要是使用起來比較復(fù)雜,雖然在讀性能上面比Mutex要好一點(diǎn)。

4.sync.Map

這個類型印象中是后來加的,最早很多人使用互斥鎖來并發(fā)的操作 map,現(xiàn)在也還有人這么寫:

typeUserstruct{
mmap[string]string
lsync.Mutex
}

也就是一個 map 配一把鎖的寫法,可能是這種寫法比較多,于是乎官方就在標(biāo)準(zhǔn)庫里面實(shí)現(xiàn)了一個sync.Map, 是一個自帶鎖的 map,使用起來方便很多,省心。

varmsync.Map

funcmain(){
m.Store("1",1)
m.Store("2",1)
m.Store("3",1)
m.Store(4,"5")//注意類型

load,ok:=m.Load("1")
ifok{
fmt.Printf("%v
",load)
}

load,ok=m.Load(4)
ifok{
fmt.Printf("%v
",load)
}
}

需要注意的一點(diǎn)是這個 map 的 key 和 value 都是 interface{}類型,所以可以隨意放入任何類型的數(shù)據(jù),在使用的時候就需要做好斷言處理。

5.sync.Once

packagemain

import"sync"

varoncesync.Once

funcmain(){
doOnce()
}

funcdoOnce(){
once.Do(func(){
println("one")
})
}

執(zhí)行結(jié)果只打印了一個 one,所以 sync.Once 的功能就是保證只執(zhí)行一次,也算是一種鎖,通??梢杂糜谥荒軋?zhí)行一次的初始化操作,比如說單例模式里面的懶漢模式可以用到。

6.sync.Cond

這個一般稱之為條件鎖,就是當(dāng)滿足某些條件下才起作用的鎖,啥個意思呢?舉個例子,當(dāng)我們執(zhí)行某個操作需要先獲取鎖,但是這個鎖必須是由某個條件觸發(fā)的,其中包含三種方式:

等待通知:wait, 阻塞當(dāng)前線程,直到收到該條件變量發(fā)來的通知

單發(fā)通知:signal, 讓該條件變量向至少一個正在等待它的通知的線程發(fā)送通知,表示共享數(shù)據(jù)的狀態(tài)已經(jīng)改變

廣播通知:broadcast, 讓條件變量給正在等待它的通知的所有線程都發(fā)送通知

下面看一個簡單的例子:

packagemain
import(
"sync"
"time"
)

varcond=sync.NewCond(&sync.Mutex{})

funcmain(){
fori:=0;i10;i++{
gofunc(iint){
cond.L.Lock()
cond.Wait()//等待通知,阻塞當(dāng)前goroutine
println(i)
cond.L.Unlock()
}(i)
}

//確保所有協(xié)程啟動完畢
time.Sleep(time.Second*1)

cond.Signal()

//確保結(jié)果有時間輸出
time.Sleep(time.Second*1)
}

開始我們使用 for 循環(huán)啟動 10 個協(xié)程,每個協(xié)程都在等待鎖,然后使用 signal 發(fā)送一個通知。

如果你多次運(yùn)行,你會發(fā)現(xiàn)打印的結(jié)果也是隨機(jī)從 0 到 9,說明各個協(xié)程之間是競爭的,鎖是起到作用的。如果把 singal 替換成 broadcast,則會打印所有結(jié)果。

講實(shí)話,我暫時也沒有發(fā)現(xiàn)有哪些應(yīng)用場景,感覺這個應(yīng)該適合需要非常精細(xì)的協(xié)程控制場景,大家先了解一下吧。

7.sync.WaitGroup

這個大多數(shù)人都用過,一般用來控制協(xié)程執(zhí)行順序,大家都知道如果我們直接用 go 啟動一個協(xié)程,比如下面這個寫法:

gofunc(){
println("1")
}()

time.Sleep(time.Second*1)//睡眠1s

如果沒有后面的 sleep 操作,協(xié)程就得不到執(zhí)行,因?yàn)檎麄€函數(shù)結(jié)束了,主進(jìn)程都結(jié)束了協(xié)程哪有時間執(zhí)行,所以有時候?yàn)榱朔奖憧梢灾苯雍唵未直┑乃邘酌?,但是?shí)際應(yīng)用中不可行。這時候就可以使用 waitGroup 解決這個問題,舉個例子:

packagemain

import"sync"

varwgsync.WaitGroup

funcmain(){
fori:=0;i10;i++{
wg.Add(1)//計數(shù)+1
gofunc(){
println("1")
wg.Done()//計數(shù)-1,相當(dāng)于wg.add(-1)
}()
}
wg.Wait()//阻塞帶等待所有協(xié)程執(zhí)行完畢
}

8.sync.Pool

這是一個池子,但是卻是一個不怎么可靠的池子,sync.Pool 初衷是用來保存和復(fù)用臨時對象,以減少內(nèi)存分配,降低 CG 壓力。

說它不可靠是指放進(jìn) Pool 中的對象,會在說不準(zhǔn)什么時候被 GC 回收掉,所以如果事先 Put 進(jìn)去 100 個對象,下次 Get 的時候發(fā)現(xiàn) Pool 是空也是有可能的。

packagemain

import(
"fmt"
"sync"
)

typeUserstruct{
namestring
}

varpool=sync.Pool{
New:func()interface{}{
returnUser{
name:"defaultname",
}
},
}

funcmain(){
pool.Put(User{name:"name1"})
pool.Put(User{name:"name2"})

fmt.Printf("%v
",pool.Get())//{name1}
fmt.Printf("%v
",pool.Get())//{name2}
fmt.Printf("%v
",pool.Get())//{defaultname}池子已空,會返回New的結(jié)果
}

從輸出結(jié)果可以看到,Pool 就像是一個池子,我們放進(jìn)去什么東西,但不一定可以取出來(如果中間有 GC 的話就會被清空),如果池子空了,就會使用之前定義的 New 方法返回的結(jié)果。

為什么這個池子會放到 sync 包里面呢?那是因?yàn)樗幸粋€重要的特性就是協(xié)程安全的,所以其底層自然也用到鎖機(jī)制。

至于其應(yīng)用場景,知名的 Web 框架 Gin 里面就有用到,在處理用戶的每條請求時都會為當(dāng)前請求創(chuàng)建一個上下文環(huán)境 Context,用于存儲請求信息及相應(yīng)信息等。Context 滿足長生命周期的特點(diǎn),且用戶請求也是屬于并發(fā)環(huán)境,所以對于線程安全的 Pool 非常適合用來維護(hù) Context 的臨時對象池。

編輯:jq
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Web
    Web
    +關(guān)注

    關(guān)注

    2

    文章

    1304

    瀏覽量

    74472
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4417

    瀏覽量

    67516
  • go語言
    +關(guān)注

    關(guān)注

    1

    文章

    159

    瀏覽量

    9779

原文標(biāo)題:淺談 Golang 鎖的應(yīng)用: sync包

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    Go 語言高并發(fā)服務(wù)設(shè)計與性能調(diào)優(yōu)實(shí)戰(zhàn):從萬級到百萬級并發(fā)的演進(jìn)之路

    在2026年的今天,Go 語言已成為高并發(fā)后端服務(wù)的首選語言。根據(jù) Stack Overflow 最新開發(fā)者調(diào)查: 指標(biāo) 數(shù)據(jù) Go 語言
    發(fā)表于 02-18 19:19

    請問C語言中整形溢出對哪些應(yīng)用場景影響較大?

    C語言中整形溢出對哪些應(yīng)用場景影響較大
    發(fā)表于 12-24 08:24

    低成本TLI4971/TLE4971電流傳感器評估套件——MS2Go與S2Go

    低成本TLI4971/TLE4971電流傳感器評估套件——MS2Go與S2Go 在電子工程師的日常工作,電流傳感器的評估和應(yīng)用是一個重要的環(huán)節(jié)。今天我們要介紹的是英飛凌(Infineon
    的頭像 發(fā)表于 12-19 16:50 ?833次閱讀

    探索TLE493D-P3XX-MS2GO 3D 2Go套件:開啟3D磁傳感器評估之旅

    探索TLE493D-P3XX-MS2GO 3D 2Go套件:開啟3D磁傳感器評估之旅 在電子工程師的日常工作,評估和開發(fā)磁傳感器是一項常見且重要的任務(wù)。英飛凌(Infineon
    的頭像 發(fā)表于 12-18 17:15 ?830次閱讀

    智能指紋CW32系列有哪些應(yīng)用場景?

    智能指紋CW32系列有哪些應(yīng)用場景
    發(fā)表于 12-05 06:34

    霍爾元件在電子里是如何應(yīng)用的?幾顆?

    霍爾元件在電子主要通過感知磁場變化實(shí)現(xiàn)狀態(tài)檢測、自動控制、防撬報警等功能,其應(yīng)用數(shù)量取決于具體設(shè)計需求,通常為1-3顆,復(fù)雜系統(tǒng)可能更多。以下是對其應(yīng)用原理和具體數(shù)量的詳細(xì)分析: 霍爾元件在
    的頭像 發(fā)表于 11-25 15:01 ?452次閱讀

    家用安防場景介紹

    家?安防?業(yè),針對家?監(jiān)控場景的安防產(chǎn)品業(yè)務(wù),主要涉及帶有攝像功能的智能家居相關(guān)產(chǎn)品, 含攝像頭、智能?、電?貓眼、掃地機(jī)器?、?辦玩具、?地/桌?機(jī)器?等。
    發(fā)表于 10-15 17:10 ?0次下載

    官網(wǎng)nrf24l01的例程demo會出現(xiàn)互斥報錯是為什么?

    我在用nrf24L01官網(wǎng)下載的例程,stm32進(jìn)行開發(fā),串口助手顯示互斥錯誤,有朋友遇到這個問題嗎
    發(fā)表于 09-10 06:05

    時空壺W4Pro:商務(wù)跨語言場景的高效溝通解決方案

    在全球化商務(wù)交流日益頻繁的當(dāng)下,跨語言溝通的效率與準(zhǔn)確性直接影響合作推進(jìn)節(jié)奏。時空壺W4ProAI同傳耳機(jī)憑借針對性的技術(shù)優(yōu)化與功能設(shè)計,在面對面商務(wù)洽談、跨國行業(yè)展會等核心商務(wù)場景,為用戶提供
    的頭像 發(fā)表于 09-08 16:53 ?1325次閱讀
    時空壺W4Pro:商務(wù)跨<b class='flag-5'>語言</b><b class='flag-5'>場景</b>的高效溝通解決方案

    奧比光助力支付寶碰一落地電梯場景

    近日,支付寶與分眾傳媒宣布聯(lián)合推出“碰一搶紅包”服務(wù)。作為創(chuàng)新交互方式,“支付寶碰一”首次被引入至電梯場景,并已在全國20余個城市的電梯鋪設(shè)。奧比光作為“支付寶碰一
    的頭像 發(fā)表于 08-12 11:32 ?1229次閱讀

    【HZ-T536開發(fā)板免費(fèi)體驗(yàn)】2 - 交叉編譯倉頡編程語言程序到開發(fā)板運(yùn)行

    感謝電子發(fā)燒友和合眾恒躍提供的試用機(jī)會。 引言 上一篇介紹了開箱的結(jié)果,接下來測試一華為倉頡編程語言在開發(fā)板上運(yùn)行的效果。最近對華為倉頡編程語言非常感興趣,所以此次測試重點(diǎn)也是看看倉頡編程
    發(fā)表于 07-16 21:27

    大家都在用什么AI軟件?有沒有好用的免費(fèi)的AI軟件推薦一?

    大家都在用什么AI軟件?有沒有好用的免費(fèi)的AI軟件推薦一?直接發(fā)個安裝,謝謝。比如deepseek、Chatgpt、豆包、阿里AI、百度AI、騰訊AI,哪個能用、好用?沒找到安裝
    發(fā)表于 07-09 18:30

    離在線語音芯片WT2606A在智能行業(yè)的應(yīng)用場景

    離在線語音芯片 WT2606A 在智能行業(yè)的應(yīng)用場景主要圍繞語音交互升級、功能擴(kuò)展及用戶體驗(yàn)優(yōu)化展開,具體包括以下核心方向: 一、基礎(chǔ)語音提示與操作引導(dǎo) 開鎖流程交互 用戶通過指紋、密碼或人臉識別
    的頭像 發(fā)表于 06-24 17:46 ?662次閱讀
    離在線語音芯片WT2606A在智能<b class='flag-5'>鎖</b>行業(yè)的應(yīng)用<b class='flag-5'>場景</b>

    HarmonyOS優(yōu)化應(yīng)用體積大小問題性能優(yōu)化

    了HAR2和HAR3,打包后,AppHAR2和HAR3存在多份重復(fù)拷貝,體積較大。 這種場景,推薦開發(fā)者使用HSP代替HAR實(shí)現(xiàn)代碼和資源共享。如下圖示例,使用HSP2對原應(yīng)用進(jìn)
    發(fā)表于 05-20 14:50

    從 Java 到 Go:面向?qū)ο蟮木奕伺c云原生的輕騎兵

    Go 語言在 2009 年被 Google 推出,在創(chuàng)建之初便明確提出了“少即是多(Less is more)”的設(shè)計原則,強(qiáng)調(diào)“以工程效率為核心,極簡規(guī)則解決復(fù)雜問題”。它與 Java
    的頭像 發(fā)表于 04-25 11:13 ?649次閱讀