Golang map并发 读写锁
本文内容纲要:
-原文链接
golang并发
一:只有写操作
var(
countint
l=sync.Mutex{}
m=make(map[int]int)
)
//全局变量并发写导致计数错误
funcvari(){
fori:=0;i<10000;i++{
gofunc(iint){
//deferl.Unlock()
//l.Lock()
count++
}(i)
}
fmt.Println(count)
}
//map并发写不加锁fatalerror:concurrentmapwrites
funcmp(){
fori:=0;i<1000;i++{
gofunc(){
deferl.Unlock()
l.Lock()
m[0]=0
}()
}
}
funcmain(){
//vari()
mp()
time.Sleep(3*time.Second)
}
sync.Mutex互斥锁多个groutine在同一时间只能有一个获取到互斥锁
二:读写都有
//不加锁的话有可能是读的错误的值
funcread(){
deferrwL.RUnlock()
rwL.RLock()
fmt.Println("read",m[0])
}
//如果不加锁会报错fatalerror:concurrentmapwrites
funcwrite(){
deferrwL.Unlock()
rwL.Lock()
m[0]=m[0]+1
}
funcrwLock(){
fori:=0;i<10000;i++{
goread()
}
fori:=0;i<10000;i++{
gowrite()
}
}
funcmain(){
//vari()
//mp()
rwLock()
time.Sleep(3*time.Second)
}
同时只能有一个goroutine能够获得写锁定同时可以有任意多个gorouinte获得读锁定同时只能存在写锁定或读锁定(读和写互斥)。
一:Question
Whenmorethanonethread*needstomutatethesamevalue,alockingmechanismisneededtosynchronizesaccess.
Withoutittwoormorethreads*couldbewritingtothesamevalueatthesametime,resultingincorruptmemory
thattypicallyresultsinacrash.
Theatomicpackageprovidesafastandeasywaytosynchronizeaccesstoprimitivevalues.Foracounteritisthefastestsynchronizationmethod.
Ithasmethodswithwelldefinedusecases,suchasincrementing,decrementing,swapping,etc.
Thesyncpackageprovidesawaytosynchronizeaccesstomorecomplicatedvalues,suchasmaps,slices,arrays,orgroupsofvalues.
Youusethisforusecasesthatarenotdefinedinatomic.
Ineithercaselockingisonlyrequiredwhenwriting.Multiplethreads*cansafelyreadthesamevaluewithoutalockingmechanism.
1.Letstakealookatthecodeyouprovided.
typeStatstruct{
countersmap[string]*int64
countersLocksync.RWMutex
averagesmap[string]*int64
averagesLocksync.RWMutex
}
func(s*Stat)Count(namestring){
s.countersLock.RLock()
counter:=s.counters[name]
s.countersLock.RUnlock()
ifcounter!=nil{
atomic.AddInt64(counter,int64(1))
return
}
}
2.What'smissinghereishowthemap'sthemselvesareinitialized.Andsofarthemapsarenotbeingmutated.Ifthecounternamesarepredeterminedandcannotbeaddedtolater,youdon'tneedtheRWMutex.Thatcodemightlooksomethinglikethis:
typeStatstruct{
countersmap[string]*int64
}
funcInitStat(names...string)Stat{
counters:=make(map[string]*int64)
for_,name:=rangenames{
counter:=int64(0)
counters[name]=&counter
}
returnStat{counters}
}
func(s*Stat)Count(namestring)int64{
counter:=s.counters[name]
ifcounter==nil{
return-1//(int64,error)instead?
}
returnatomic.AddInt64(counter,1)
}
(Note:Iremovedaveragesbecauseitwasn'tbeingusedintheoriginalexample.)
Now,letssayyoudidn'twantyourcounterstobepredetermined.Inthatcaseyouwouldneedamutextosynchronizeaccess.
3.LetstryitwithjustaMutex.It'ssimplebecauseonlyonethread*canholdLockatatime.Ifasecondthread*triestoLockbeforethefirstreleasestheirswithUnlock,itwaits(orblocks)**untilthen.
typeStatstruct{
countersmap[string]*int64
mutexsync.Mutex
}
funcInitStat()Stat{
returnStat{counters:make(map[string]*int64)}
}
func(s*Stat)Count(namestring)int64{
s.mutex.Lock()
counter:=s.counters[name]
ifcounter==nil{
value:=int64(0)
counter=&value
s.counters[name]=counter
}
s.mutex.Unlock()
returnatomic.AddInt64(counter,1)
}
二:Thecodeabovewillworkjustfine.Buttherearetwoproblems.
1.IfthereisapanicbetweenLock()andUnlock()themutexwillbelockedforever,evenifyouweretorecoverfromthepanic.Thiscodeprobablywon'tpanic,butingeneralit'sbetterpracticetoassumeitmight.
Problem#1iseasytosolve.Usedefer:
```
func(s*Stat)Count(namestring)int64{
s.mutex.Lock()
defers.mutex.Unlock()
counter:=s.counters[name]
ifcounter==nil{
value:=int64(0)
counter=&value
s.counters[name]=counter
}
returnatomic.AddInt64(counter,1)
}
```
ThisensuresthatUnlock()isalwayscalled.Andifforsomereasonyouhavemorethenonereturn,youonlyneedtospecifyUnlock()onceattheheadofthefunction.
2.Anexclusivelockistakenwhilefetchingthecounter.Onlyonethread*canreadfromthecounteratonetime.
Problem#2canbesolvedwithRWMutex.Howdoesitworkexactly,andwhyisituseful?
RWMutexisanextensionofMutexandaddstwomethods:RLockandRUnlock.ThereareafewpointsthatareimportanttonoteaboutRWMutex:
RLockisasharedreadlock.Whenalockistakenwithit,otherthreads*canalsotaketheirownlockwithRLock.Thismeansmultiplethreads*canreadatthesametime.It'ssemi-exclusive.
Ifthemutexisreadlocked,acalltoLockisblocked**.Ifoneormorereadersholdalock,youcannotwrite.
Ifthemutexiswritelocked(withLock),RLockwillblock**.
AgoodwaytothinkaboutitisRWMutexisaMutexwithareadercounter.RLockincrementsthecounterwhileRUnlockdecrementsit.AcalltoLockwillblockaslongasthatcounteris>0.
Youmaybethinking:Ifmyapplicationisreadheavy,wouldthatmeanawritercouldbeblockedindefinitely?No.ThereisonemoreusefulpropertyofRWMutex:
Ifthereadercounteris>0andLockiscalled,futurecallstoRLockwillalsoblockuntiltheexistingreadershavereleasedtheirlocks,thewriterhasobtainedhislockandlaterreleasesit.
Thinkofitasthelightabovearegisteratthegrocerystorethatsaysacashierisopenornot.Thepeopleinlinegettostaythereandtheywillbehelped,butnewpeoplecannotgetinline.Assoonasthelastremainingcustomerishelpedthecashiergoesonbreak,andthatregistereitherremainscloseduntiltheycomebackortheyarereplacedwithadifferentcashier.
3.LetsmodifytheearlierexamplewithanRWMutex:
typeStatstruct{
countersmap[string]*int64
mutexsync.RWMutex
}
funcInitStat()Stat{
returnStat{counters:make(map[string]*int64)}
}
func(s*Stat)Count(namestring)int64{
varcounter*int64
ifcounter=getCounter(name);counter==nil{
counter=initCounter(name);
}
returnatomic.AddInt64(counter,1)
}
func(s*Stat)getCounter(namestring)*int64{
s.mutex.RLock()
defers.mutex.RUnlock()
returns.counters[name]
}
func(s*Stat)initCounter(namestring)*int64{
s.mutex.Lock()
defers.mutex.Unlock()
counter:=s.counters[name]
ifcounter==nil{
value:=int64(0)
counter=&value
s.counters[name]=counter
}
returncounter
}
三:WiththecodeaboveI'veseparatedthelogicoutintogetCounterandinitCounterfunctionsto:
Keepthecodesimpletounderstand.ItwouldbedifficulttoRLock()andLock()inthesamefunction.
Releasethelocksasearlyaspossiblewhileusingdefer.
Thecodeabove,unliketheMutexexample,allowsyoutoincrementdifferentcounterssimultaneously.
AnotherthingIwantedtopointoutiswithalltheexamplesabove,themapmap[string]*int64containspointerstothecounters,notthecountersthemselves.Ifyouweretostorethecountersinthemapmap[string]int64youwouldneedtouseMutexwithoutatomic.Thatcodewouldlooksomethinglikethis:
typeStatstruct{
countersmap[string]int64
mutexsync.Mutex
}
funcInitStat()Stat{
returnStat{counters:make(map[string]int64)}
}
func(s*Stat)Count(namestring)int64{
s.mutex.Lock()
defers.mutex.Unlock()
s.counters[name]++
returns.counters[name]
}
Youmaywanttodothistoreducegarbagecollection-butthatwouldonlymatterifyouhadthousandsofcounters-andeventhenthecountersthemselvesdon'ttakeupawholelotofspace(comparedtosomethinglikeabytebuffer).
- WhenIsaythreadImeango-routine.Athreadinotherlanguagesisamechanismforrunningoneormoresetsofcodesimultaneously.Athreadisexpensivetocreateandtear-down.Ago-routineisbuiltontopofthreads,butre-usesthem.Whenago-routinesleepstheunderlyingthreadcanbeusedbyanothergo-routine.Whenago-routinewakesup,itmightbeonadifferentthread.Gohandlesallthisbehindthescenes.--Butforallintentsandpurposesyouwouldtreatago-routinelikeathreadwhenitcomestomemoryaccess.However,youdon'thavetobeasconservativewhenusinggo-routinesasyoudothreads.
**Whenago-routineisblockedbyLock,RLock,achannel,orSleep,theunderlyingthreadmightbere-used.Nocpuisusedbythatgo-routine-thinkofitaswaitinginline.Likeotherlanguagesaninfinitelooplikefor{}wouldblockwhilekeepingthecpuandgo-routinebusy-thinkofthatasrunningaroundinacircle-you'llgetdizzy,throwup,andthepeoplearoundyouwon'tbeveryhappy.
原文链接
https://stackoverflow.com/questions/19148809/how-to-use-rwmutex-in-golang
本文内容总结:原文链接,
原文链接:https://www.cnblogs.com/alin-qu/p/10632555.html