1.Go并åç¼ç¨ â sync.Once
2.Go并åç¼ç¨ï¼goroutineï¼channelåsync详解
3.Rust并发:标准库sync::Once源码分析
4.Go并发编程之原子操作sync/atomic
5.Golang sync.Cond 条件变量源码分析
6.深度解析sync WaitGroup源码
Go并åç¼ç¨ â sync.Once
ç®ä»
Once å¯ä»¥ç¨æ¥æ§è¡æ个å½æ°ï¼ä½æ¯è¿ä¸ªå½æ°ä» ä» åªä¼æ§è¡ä¸æ¬¡ï¼å¸¸å¸¸ç¨äºåä¾å¯¹è±¡çåå§ååºæ¯ã说å°è¿ï¼å°±ä¸å¾ä¸è¯´ä¸ä¸åä¾æ¨¡å¼äºã
åä¾æ¨¡å¼åä¾æ¨¡å¼æææ±å¼å饿æ±å¼ä¸¤ç§ï¼ä¸ä»£ç ã
饿æ±å¼é¥¿æ±å¼é¡¾åæä¹å°±æ¯æ¯è¾é¥¥é¥¿ï¼æ以就æ¯ä¸æ¥å°±åå§åã
var?源码instance?=?&Singleton{ }type?Singleton?struct?{ }func?GetInstance()?*Singleton?{ ?return?instance}ææ±å¼ææ±å¼é¡¾åæä¹å°±æ¯å·æï¼å¨è·åå®ä¾çæ¶åå¨è¿è¡åå§åï¼ä½æ¯ææ±å¼ä¼æ并åé®é¢ã并åé®é¢ä¸»è¦åçå¨ instance == nil è¿ä¸ªå¤ææ¡ä»¶ä¸ï¼æå¯è½å¤ä¸ª goruntine åæ¶è·å instance 对象é½æ¯ nil ï¼ç¶åé½å¼å§åå»ºäº Singleton å®ä¾ï¼å°±ä¸æ»¡è¶³åä¾æ¨¡å¼äºã
var?instance?*Singletontype?Singleton?struct?{ }func?GetInstance()?*Singleton?{ ?if?instance?==?nil?{ instance?=?&Singleton{ }?}?return?instance}å éæ们é½ç¥é并åé®é¢åºç°åï¼å¯ä»¥éè¿å éæ¥è¿è¡è§£å³ï¼å¯ä»¥ä½¿ç¨ sync.Metux æ¥å¯¹æ´ä¸ªæ¹æ³è¿è¡å éï¼å°±ä¾å¦ä¸é¢è¿æ ·ãè¿ç§æ¹å¼æ¯è§£å³äºå¹¶åçé®é¢ï¼ä½æ¯éçç²åº¦æ¯è¾é«ï¼æ¯æ¬¡è°ç¨ GetInstance æ¹æ³çæ¶åé½éè¦è·å¾éæè½è·å¾ instance å®ä¾ï¼å¦æå¨è°ç¨é¢çæ¯è¾é«çåºæ¯ä¸æ§è½å°±ä¸ä¼å¾å¥½ãé£æä»ä¹æ¹å¼å¯ä»¥è§£å³åï¼è®©æ们æ¥çå¾ä¸çå§
var?mutex?sync.Mutexvar?instance?*Singletontype?Singleton?struct?{ }func?GetInstance()?*Singleton?{ ?mutex.Lock()?defer?mutex.Unlock()?if?instance?==?nil?{ instance?=?&Singleton{ }?}?return?instance}Double Check为äºè§£å³éçç²åº¦é®é¢ï¼æ们å¯ä»¥ä½¿ç¨ Double Check çæ¹å¼æ¥è¿è¡è§£å³ï¼ä¾å¦ä¸é¢ç代ç ï¼ç¬¬ä¸æ¬¡å¤æ instance == nil ä¹åéè¦è¿è¡å éæä½ï¼ç¶åå第äºæ¬¡å¤æ instance == nil ä¹åæè½å建å®ä¾ãè¿ç§æ¹å¼å¯¹æ¯ä¸é¢çæ¡ä¾æ¥è¯´ï¼éçç²åº¦æ´ä½ï¼å 为å¦æ instance != nil çæ åµä¸æ¯ä¸éè¦å éçãä½æ¯è¿ç§æ¹å¼å®ç°èµ·æ¥æ¯ä¸æ¯æ¯è¾éº»ç¦ï¼æ没æä»ä¹æ¹å¼å¯ä»¥è§£å³å¢ï¼
var?mutex?sync.Mutexvar?instance?*Singletontype?Singleton?struct?{ }func?GetInstance()?*Singleton?{ ?if?instance?==?nil?{ mutex.Lock()defer?mutex.Unlock()if?instance?==?nil?{ ?instance?=?&Singleton{ }}?}?return?instance}ä½¿ç¨ sync.Onceå¯ä»¥ä½¿ç¨ sync.Once æ¥å®ç°åä¾çåå§åé»è¾ï¼å 为è¿ä¸ªé»è¾è³å¤åªä¼è·ä¸æ¬¡ãæ¨è使ç¨è¿ç§æ¹å¼æ¥è¿è¡åä¾çåå§åï¼å½ç¶ä¹å¯ä»¥ä½¿ç¨é¥¿æ±å¼ã
var?once?sync.Oncevar?instance?*Singletontype?Singleton?struct?{ }func?GetInstance()?*Singleton?{ ?once.Do(func()?{ instance?=?&Singleton{ }?})?return?instance}æºç åæä¸é¢å°±æ¯ sync.Once å çæºç ï¼æå é¤äºæ³¨éï¼ä»£ç ä¸å¤ï¼Once æ°æ®ç»æ主è¦ç± done å m ç»æï¼å ¶ä¸ done æ¯åå¨ f å½æ°æ¯å¦å·²æ§è¡ï¼m æ¯ä¸ä¸ªéå®ä¾ã
type?Once?struct?{ ?done?uint?//?få½æ°æ¯å¦å·²æ§è¡?mMutex//?é}func?(o?*Once)?Do(f?func())?{ ?if?atomic.LoadUint(&o.done)?==?0?{ o.doSlow(f)?}}func?(o?*Once)?doSlow(f?func())?{ ?o.m.Lock()?defer?o.m.Unlock()?if?o.done?==?0?{ defer?atomic.StoreUint(&o.done,?1)f()?}}Do æ¹æ³
ä¼ å ¥ä¸ä¸ª functionï¼ç¶å sync.Once æ¥ä¿è¯åªæ§è¡ä¸æ¬¡ï¼å¨ Do æ¹æ³ä¸ä½¿ç¨ atomic æ¥è¯»å done åéï¼å¦ææ¯ 0 ï¼å°±ä»£ç f å½æ°æ²¡æ被æ§è¡è¿ï¼ç¶åå°±è°ç¨ doSlowæ¹æ³ï¼ä¼ å ¥ f å½æ°
doShow æ¹æ³
doShow ç第ä¸ä¸ªæ¥éª¤å°±æ¯å å éï¼è¿éå éçç®çæ¯ä¿è¯åä¸æ¶å»æ¯è½ç±ä¸ä¸ª goruntine æ¥æ§è¡ doSlow æ¹æ³ï¼ç¶åå次å¤æ done æ¯å¦æ¯ 0 ï¼è¿ä¸ªå¤æå°±ç¸å½äºæ们ä¸é¢è¯´ç DoubleCheck ï¼å 为 doSlow å¯è½åå¨å¹¶åé®é¢ãç¶åæ§è¡ f æ¹æ³ï¼ç¶åæ§è¡ä½¿ç¨ atomic å° done ä¿åæ 1ãä½¿ç¨ DoubleCheck ä¿è¯äº f æ¹æ³åªä¼è¢«æ§è¡ä¸æ¬¡ã
æ¥ççï¼é£å¯ä»¥è¿æ ·å®ç° sync.Once åï¼
è¿æ ·ä¸æ¯æ´ç®åä¸ç¹åï¼ä½¿ç¨ååç CAS æä½å°±å¯ä»¥è§£å³å¹¶åé®é¢åï¼å¹¶ååªæ§è¡ä¸æ¬¡ f æ¹æ³çé®é¢æ¯å¯ä»¥è§£å³ï¼ä½æ¯ Do æ¹æ³å¯è½å¹¶åï¼ç¬¬ä¸ä¸ªè°ç¨è å° done 设置æäº 1 ç¶åè°ç¨ f æ¹æ³ï¼å¦æ f æ¹æ³ç¹å«èæ¶é´ï¼é£ä¹ç¬¬äºä¸ªè°ç¨è è·åå° done 为 1 å°±ç´æ¥è¿åäºï¼æ¤æ¶ fæ¹æ³æ¯æ²¡ææ§è¡è¿ç¬¬äºæ¬¡ï¼ä½æ¯æ¤æ¶ç¬¬äºä¸ªè°ç¨è å¯ä»¥ç»§ç»æ§è¡åé¢ç代ç ï¼å¦æåé¢ç代ç ä¸æç¨å° f æ¹æ³å建çå®ä¾ï¼ä½æ¯ç±äº f æ¹æ³è¿å¨æ§è¡ä¸ï¼æ以å¯è½ä¼åºç°æ¥éé®é¢ãæ以å®æ¹éç¨çæ¯Lock + DoubleCheck çæ¹å¼ã
if?atomic.CompareAndSwapUint(&o.done,?0,?1)?{ f()}æå±æ§è¡å¼å¸¸åå¯ç»§ç»æ§è¡çOnce
çæäºæºç ä¹åï¼æ们就å¯ä»¥æ©å± sync.Once å äºãä¾å¦ f æ¹æ³å¨æ§è¡çæ¶åæ¥éäºï¼ä¾å¦è¿æ¥åå§å失败ï¼æä¹åï¼æ们å¯ä»¥å®ç°ä¸ä¸ªé«çº§çæ¬ç Once å ï¼å ·ä½ç slowDo 代ç å¯ä»¥åèä¸é¢çå®ç°
func?(o?*Once)?slowDo(f?func()?error)?error?{ o.m.Lock()defer?o.m.Unlock()var?err?errorif?o.done?==?0?{ ?//?Double?Checkerr?=?f()if?err?==?nil?{ //?没æå¼å¸¸çæ¶åè®°å½doneå¼?atomic.StoreUint(&o.done,?1)}}return?err}带æ§è¡ç»æç Once
ç±äº Once æ¯ä¸å¸¦æ§è¡ç»æçï¼æ们ä¸ç¥é Once ä»ä¹æ¶åä¼æ§è¡ç»æï¼å¦æåå¨å¹¶åï¼éè¦ç¥éæ¯å¦æ§è¡æåçè¯ï¼å¯ä»¥çä¸ä¸é¢çæ¡ä¾ï¼æè¿éæ¯ä»¥ redis è¿æ¥çé®é¢æ¥è¿è¡è¯´æçãDo æ¹æ³æ§è¡å®æ¯åå° init å¼è®¾ç½®æ 1 ï¼ç¶åå ¶ä» goruntine å¯ä»¥éè¿ IsConnetion æ¥è·åè¿æ¥æ¯å¦å»ºç«ï¼ç¶åååç»çæä½ã
type?RedisConn?struct?{ ?once?sync.Once?init?uint}func?(this?*RedisConn)?Init()?{ ?this.once.Do(func()?{ //?do?redis?connectionatomic.StoreUint(&this.init,?1)?})}func?(this?*RedisConn)?IsConnect()?bool?{ ?//?å¦å¤ä¸ä¸ªgoroutine?return?atomic.LoadUint(&this.init)?!=?0}Go并åç¼ç¨ï¼goroutineï¼channelåsync详解
ä¼é ç并åç¼ç¨èå¼ï¼å®åç并åæ¯æï¼åºè²ç并åæ§è½æ¯Goè¯è¨åºå«äºå ¶ä»è¯è¨çä¸å¤§ç¹è²ãå¨å½ä»è¿ä¸ªå¤æ ¸æ¶ä»£ï¼å¹¶åç¼ç¨çæä¹ä¸è¨èå»ã使ç¨Goå¼å并åç¨åºï¼æä½èµ·æ¥é常ç®åï¼è¯è¨çº§å«æä¾å ³é®ågoç¨äºå¯å¨åç¨ï¼å¹¶ä¸å¨åä¸å°æºå¨ä¸å¯ä»¥å¯å¨æåä¸ä¸ä¸ªåç¨ã
ä¸é¢å°±æ¥è¯¦ç»ä»ç»ã
goroutineGoè¯è¨ç并åæ§è¡ä½ç§°ä¸ºgoroutineï¼ä½¿ç¨å ³é®è¯goæ¥å¯å¨ä¸ä¸ªgoroutineã
goå ³é®è¯åé¢å¿ é¡»è·ä¸ä¸ªå½æ°ï¼å¯ä»¥æ¯æåå½æ°ï¼ä¹å¯ä»¥æ¯æ åå½æ°ï¼å½æ°çè¿åå¼ä¼è¢«å¿½ç¥ã
goçæ§è¡æ¯éé»å¡çã
å æ¥çä¸ä¸ªä¾åï¼
packagemainimport("fmt""time")funcmain(){ gospinner(*time.Millisecond)constn=fibN:=fib(n)fmt.Printf("\rFibonacci(%d)=%d\n",n,fibN)//Fibonacci()=}funcspinner(delaytime.Duration){ for{ for_,r:=range`-\|/`{ fmt.Printf("\r%c",r)time.Sleep(delay)}}}funcfib(xint)int{ ifx<2{ returnx}returnfib(x-1)+fib(x-2)}ä»æ§è¡ç»ææ¥çï¼æå计ç®åºäºææ³¢é£å¥æ°åçå¼ï¼è¯´æç¨åºå¨spinnerå¤å¹¶æ²¡æé»å¡ï¼èä¸spinnerå½æ°è¿ä¸ç´å¨å±å¹ä¸æå°æ示å符ï¼è¯´æç¨åºæ£å¨æ§è¡ã
å½è®¡ç®å®ææ³¢é£å¥æ°åçå¼ï¼mainå½æ°æå°ç»æ并éåºï¼spinnerä¹è·çéåºã
åæ¥çä¸ä¸ªä¾åï¼å¾ªç¯æ§è¡æ¬¡ï¼æå°ä¸¤ä¸ªæ°çåï¼
packagemainimport"fmt"funcAdd(x,yint){ z:=x+yfmt.Println(z)}funcmain(){ fori:=0;i<;i++{ goAdd(i,i)}}æé®é¢äºï¼å±å¹ä¸ä»ä¹é½æ²¡æï¼ä¸ºä»ä¹å¢ï¼
è¿å°±è¦çGoç¨åºçæ§è¡æºå¶äºãå½ä¸ä¸ªç¨åºå¯å¨æ¶ï¼åªæä¸ä¸ªgoroutineæ¥è°ç¨mainå½æ°ï¼ç§°ä¸ºä¸»goroutineãæ°çgoroutineéè¿goå ³é®è¯å建ï¼ç¶å并åæ§è¡ãå½mainå½æ°è¿åæ¶ï¼ä¸ä¼çå¾ å ¶ä»goroutineæ§è¡å®ï¼èæ¯ç´æ¥æ´åç»ææægoroutineã
é£æ没æåæ³è§£å³å¢ï¼å½ç¶æ¯æçï¼è¯·å¾ä¸çã
channelä¸è¬åå¤è¿ç¨ç¨åºæ¶ï¼é½ä¼éå°ä¸ä¸ªé®é¢ï¼è¿ç¨é´éä¿¡ã常è§çéä¿¡æ¹å¼æä¿¡å·ï¼å ±äº«å åçãgoroutineä¹é´çéä¿¡æºå¶æ¯ééchannelã
使ç¨makeå建ééï¼
ch:=make(chanint)//chçç±»åæ¯chanintééæ¯æä¸ä¸ªä¸»è¦æä½ï¼sendï¼receiveåcloseã
ch<-x//åéx=<-ch//æ¥æ¶<-ch//æ¥æ¶ï¼ä¸¢å¼ç»æclose(ch)//å ³éæ ç¼å²channelmakeå½æ°æ¥å两个åæ°ï¼ç¬¬äºä¸ªåæ°æ¯å¯éåæ°ï¼è¡¨ç¤ºéé容éãä¸ä¼ æè ä¼ 0表示å建äºä¸ä¸ªæ ç¼å²ééã
æ ç¼å²ééä¸çåéæä½å°ä¼é»å¡ï¼ç´å°å¦ä¸ä¸ªgoroutineå¨å¯¹åºçééä¸æ§è¡æ¥æ¶æä½ãç¸åï¼å¦ææ¥æ¶å æ§è¡ï¼é£ä¹æ¥æ¶goroutineå°ä¼é»å¡ï¼ç´å°å¦ä¸ä¸ªgoroutineå¨å¯¹åºééä¸æ§è¡åéã
æ以ï¼æ ç¼å²ééæ¯ä¸ç§åæ¥ééã
ä¸é¢æ们使ç¨æ ç¼å²ééæä¸é¢ä¾åä¸åºç°çé®é¢è§£å³ä¸ä¸ã
packagemainimport"fmt"funcAdd(x,yint,chchanint){ z:=x+ych<-z}funcmain(){ ch:=make(chanint)fori:=0;i<;i++{ goAdd(i,i,ch)}fori:=0;i<;i++{ fmt.Println(<-ch)}}å¯ä»¥æ£å¸¸è¾åºç»æã
主goroutineä¼é»å¡ï¼ç´å°è¯»åå°ééä¸çå¼ï¼ç¨åºç»§ç»æ§è¡ï¼æåéåºã
ç¼å²channelå建ä¸ä¸ªå®¹éæ¯5çç¼å²ééï¼
ch:=make(chanint,5)ç¼å²ééçåéæä½å¨ééå°¾é¨æå ¥ä¸ä¸ªå ç´ ï¼æ¥æ¶æä½ä»ééç头é¨ç§»é¤ä¸ä¸ªå ç´ ãå¦æéé满äºï¼åéä¼é»å¡ï¼ç´å°å¦ä¸ä¸ªgoroutineæ§è¡æ¥æ¶ãç¸åï¼å¦æééæ¯ç©ºçï¼æ¥æ¶ä¼é»å¡ï¼ç´å°å¦ä¸ä¸ªgoroutineæ§è¡åéã
æ没ææè§ï¼å ¶å®ç¼å²ééåéåä¸æ ·ï¼ææä½é½è§£è¦äºã
ååchannelç±»åchan<-intæ¯ä¸ä¸ªåªè½åéçééï¼ç±»å<-chanintæ¯ä¸ä¸ªåªè½æ¥æ¶çééã
ä»»ä½ååééé½å¯ä»¥ç¨ä½ååééï¼ä½åè¿æ¥ä¸è¡ã
è¿æä¸ç¹éè¦æ³¨æï¼closeåªè½ç¨å¨åéééä¸ï¼å¦æç¨å¨æ¥æ¶ééä¼æ¥éã
çä¸ä¸ªååééçä¾åï¼
packagemainimport"fmt"funccounter(outchan<-int){ forx:=0;x<;x++{ out<-x}close(out)}funcsquarer(outchan<-int,in<-chanint){ forv:=rangein{ out<-v*v}close(out)}funcprinter(in<-chanint){ forv:=rangein{ fmt.Println(v)}}funcmain(){ n:=make(chanint)s:=make(chanint)gocounter(n)gosquarer(s,n)printer(s)}syncsyncå æä¾äºä¸¤ç§éç±»åï¼sync.Mutexåsync.RWMutexï¼åè æ¯äºæ¥éï¼åè æ¯è¯»åéã
å½ä¸ä¸ªgoroutineè·åäºMutexåï¼å ¶ä»goroutineä¸ç®¡è¯»åï¼åªè½çå¾ ï¼ç´å°é被éæ¾ã
packagemainimport("fmt""sync""time")funcmain(){ varmutexsync.Mutexwg:=sync.WaitGroup{ }//主goroutineå è·åéfmt.Println("Locking(G0)")mutex.Lock()fmt.Println("locked(G0)")wg.Add(3)fori:=1;i<4;i++{ gofunc(iint){ //ç±äºä¸»goroutineå è·åéï¼ç¨åºå¼å§5ç§ä¼é»å¡å¨è¿éfmt.Printf("Locking(G%d)\n",i)mutex.Lock()fmt.Printf("locked(G%d)\n",i)time.Sleep(time.Second*2)mutex.Unlock()fmt.Printf("unlocked(G%d)\n",i)wg.Done()}(i)}//主goroutine5ç§åéæ¾étime.Sleep(time.Second*5)fmt.Println("readyunlock(G0)")mutex.Unlock()fmt.Println("unlocked(G0)")wg.Wait()}RWMutexå±äºç»å ¸çååå¤è¯»æ¨¡åï¼å½è¯»é被å ç¨æ¶ï¼ä¼é»æ¢åï¼ä½ä¸é»æ¢è¯»ãèåéä¼é»æ¢åå读ã
packagemainimport("fmt""sync""time")funcmain(){ varrwMutexsync.RWMutexwg:=sync.WaitGroup{ }Data:=0wg.Add()fori:=0;i<;i++{ gofunc(tint){ //第ä¸æ¬¡è¿è¡åï¼å解éã//循ç¯å°ç¬¬äºæ¬¡æ¶ï¼è¯»éå®åï¼goroutine没æé»å¡ï¼åæ¶è¯»æåãfmt.Println("Locking")rwMutex.RLock()deferrwMutex.RUnlock()fmt.Printf("Readdata:%v\n",Data)wg.Done()time.Sleep(2*time.Second)}(i)gofunc(tint){ //åéå®ä¸æ¯éè¦è§£éåæè½åçrwMutex.Lock()deferrwMutex.Unlock()Data+=tfmt.Printf("WriteData:%v%d\n",Data,t)wg.Done()time.Sleep(2*time.Second)}(i)}wg.Wait()}æ»ç»å¹¶åç¼ç¨ç®æ¯Goçç¹è²ï¼ä¹æ¯æ ¸å¿åè½ä¹ä¸äºï¼æ¶åçç¥è¯ç¹å ¶å®æ¯é常å¤çï¼æ¬æä¹åªæ¯èµ·å°ä¸ä¸ªæç å¼ççä½ç¨èå·²ã
æ¬æå¼å§ä»ç»äºgoroutineçç®åç¨æ³ï¼ç¶åå¼åºäºééçæ¦å¿µã
ééæä¸ç§ï¼
æ ç¼å²éé
ç¼å²éé
ååéé
æåä»ç»äºGoä¸çéæºå¶ï¼åå«æ¯syncå æä¾çsync.Mutexï¼äºæ¥éï¼åsync.RWMutexï¼è¯»åéï¼ã
goroutineå大精深ï¼åé¢çåè¿æ¯è¦æ ¢æ ¢è¸©çã
æç« ä¸çèå¾åæºç é½ä¸ä¼ å°äºGitHubï¼æéè¦çåå¦å¯èªè¡ä¸è½½ã
å°åï¼github.com/yongxinz/gopher/tree/main/sc
ä½è ï¼yongxinz
Rust并发:标准库sync::Once源码分析
一次初始化同步原语Once,其核心功能在于确保闭包仅被执行一次。源码常见应用包括FFI库初始化、源码静态变量延迟初始化等。源码
标准库中的源码Once实现更为复杂,其关键在于如何高效地模拟Mutex阻塞与唤醒机制。源码vlc源码分析(六)这一机制依赖于线程暂停和唤醒原语thread::park/unpark,源码它们是源码实现多线程同步对象如Mutex、Condvar等的源码基础。
具体实现中,源码Once维护四个内部状态,源码状态与等待队列头指针共同存储于AtomicUsize中,源码利用4字节对齐优化空间。源码
构造Once实例时,源码初始化状态为Incomplete。源码调用Once::call_once或Once::call_once_force时,分别检查是否已完成初始化,未完成则执行闭包,闭包执行路径标记为冷路径以节省资源,同时避免泛型导致的代码膨胀。
闭包执行逻辑由Once::call_inner负责,线程尝试获取执行权限,未能获取则进入等待状态,获取成功后执行闭包,结束后唤醒等待线程。
等待队列通过无锁侵入式链表实现,节点在栈上分配,以优化内存使用。看jdk源码管用Once::wait函数实现等待线程逻辑,WaiterQueue的drop方法用于唤醒所有等待线程,需按特定顺序操作栈节点,以避免use after free等潜在问题。
思考题:如何在实际项目中利用Once实现资源安全共享?如何评估Once与Mutex等同步原语在不同场景下的性能差异?
Go并发编程之原子操作sync/atomic
Go语言的并发编程中,sync/atomic包提供了底层的原子内存操作,用于处理并发环境中的数据同步和冲突避免。这个包利用了CPU的原子操作指令,确保在并发情况下,对变量的操作是线程安全的。然而,官方建议仅在必要且确实涉及底层操作时使用,如避免使用channel或sync包中的锁的场景。
sync/atomic包的核心是5种基本数据类型的原子操作:add(只支持int、int、uint、uint和uintptr),以及一个扩展的Value类型,后者在1.4版本后支持Load、Store、CompareAndSwap和Swap方法,可用于操作任意类型的数据。Value类型尤其重要,因为它扩展了原子操作的适用范围。
具体来说,swap操作(如SwapInt)用于原子地替换内存中的值,compare-and-swap(CAS)则检查并替换值,通信指标源码公式如果当前值与预期值一致。add操作(如AddInt)则进行加法操作并返回新值,而load、store操作分别用于读取和写入值,如LoadInt和StoreInt。
在实际使用时,例如对map的并发读写,可以通过Value类型避免加锁。sync/atomic的相关源码和示例可在GitHub的教程[1]和作者的个人网站[2]中找到。至于进一步学习,可以关注公众号coding进阶获取更多资源,或者在知乎[3]上查找无忌的资料。
参考资料:
[1] Go语言初级、中级和高级教程: github.com/jincheng9/go...
[2] Jincheng's Blog: jincheng9.github.io/
[3] 无忌: zhihu.com/people/thucuh...
Golang sync.Cond 条件变量源码分析
sync.Cond 是 Golang 标准库 sync 包中一个关键的条件变量类型,用于在多个goroutine间协调等待特定条件。它常用于生产者-消费者模型等场景,确保在某些条件满足后才能继续执行。本文基于 go-1. 源码,深入解析 sync.Cond 的核心机制与用法。
sync.Cond 的基本用法包括创建条件变量、等待唤醒与发送信号。使用时,通常涉及到一个互斥锁(Locker)以确保并发安全性。首先,通过`sync.NewCond(l Locker)`创建条件变量。其次,`cond.Wait()`使当前执行的防止页面源码复制goroutine等待直到被唤醒,期间会释放锁并暂停执行。`cond.Signal()`和`Broadcast()`用于唤醒等待的goroutine,前者唤醒一个,后者唤醒所有。
在底层实现中,sync.Cond 采用了一种称为 notifyList 的数据结构来管理等待和唤醒过程。notifyList 由一组元素构成,其中`wait`和`notify`表示当前最大ticket值和已唤醒的最大ticket值,而`head`和`tail`则分别代表等待的goroutine链表的头和尾。在`Wait`操作中,每次调用`runtime_notifyListAdd`生成唯一的ticket,并将当前goroutine添加到链表中。当调用`Signal`或`Broadcast`时,会查找并唤醒当前`notify`值对应的等待goroutine,并更新`notify`值。
信号唤醒过程确保了FIFO的顺序,即最早等待的goroutine会首先被唤醒。这种机制有效地防止了并发操作下列表的乱序,确保了正确的唤醒顺序,尽管在实际执行中,遍历整个列表的过程在大多数情况下效率较高。
在使用sync.Cond时,需注意避免潜在的死锁风险和错误的唤醒顺序。确保合理管理互斥锁的使用,以及在适当情况下使用`Signal`或`Broadcast`来唤醒等待的goroutine。正确理解和应用sync.Cond,能有效提升并发编程的源码精灵网页版效率与稳定性。
深度解析sync WaitGroup源码
waitGroup
waitGroup 是 Go 语言中并发编程中常用的语法之一,主要用于解决并发和等待问题。它是 sync 包下的一个子组件,特别适用于需要协调多个goroutine执行任务的场景。
waitGroup 主要用于解决goroutine间的等待关系。例如,goroutineA需要在等待goroutineB和goroutineC这两个子goroutine执行完毕后,才能执行后续的业务逻辑。通过使用waitGroup,goroutineA在执行任务时,会在检查点等待其他goroutine完成,确保所有任务执行完毕后,goroutineA才能继续进行。
在实现上,waitGroup 通过三个方法来操作:Add、Done 和 Wait。Add方法用于增加计数,Done方法用于减少计数,Wait方法则用于在计数为零时阻塞等待。这些方法通过原子操作实现同步安全。
waitGroup的源码实现相对简洁,主要涉及数据结构设计和原子操作。数据结构包括了一个 noCopy 的辅助字段以及一个复合意义的 state1 字段。state1 字段的组成根据目标平台的不同(位或位)而有所不同。在位环境下,state1的第一个元素是等待线程数,第二个元素是 waitGroup 计数值,第三个元素是信号量。而在位环境下,如果 state1 的地址不是位对齐的,那么 state1 的第一个元素是信号量,后两个元素分别是等待线程数和计数值。
waitGroup 的核心方法 Add 和 Wait 的实现原理如下:
Add方法通过原子操作增加计数值。当执行 Add 方法时,首先将 delta 参数左移位,然后通过原子操作将其添加到计数值上。需要注意的是,delta 的值可正可负,用于在调用 Done 方法时减少计数值。
Done方法通过调用 Add(-1)来减少计数值。
Wait方法则持续检查 state 值。当计数值为零时,表示所有子goroutine已完成,调用者无需等待。如果计数值大于零,则调用者会变成等待者,加入等待队列,并阻塞自己,直到所有任务执行完毕。
通过使用waitGroup,开发者可以轻松地协调和同步并发任务的执行,确保所有任务按预期顺序完成。这在多goroutine协同工作时,尤其重要。掌握waitGroup的使用和源码实现,将有助于提高并发编程的效率和可维护性。
如果您对并发编程感兴趣,希望持续关注相关技术更新,请通过微信搜索「迈莫coding」,第一时间获取更多深度解析和实战指南。
PyTorch 源码解读之 BN & SyncBN:BN 与 多卡同步 BN 详解
BatchNorm原理 BatchNorm最早在全连接网络中提出,旨在对每个神经元的输入进行归一化操作。在卷积神经网络(CNN)中,这一原理被扩展为对每个卷积核的输入进行归一化,即在channel维度之外的所有维度上进行归一化。BatchNorm带来的优势包括提高网络的收敛速度、稳定训练过程、减少过拟合现象等。 BatchNorm的数学表达式为公式[1],引入缩放因子γ和移位因子β,作者在文章中解释了它们的作用。 PyTorch中与BatchNorm相关的类主要位于torch.nn.modules.batchnorm模块中,包括如下的类:_NormBase、BatchNormNd。 具体实现细节如下: _NormBase类定义了BN相关的一些属性。 初始化过程。 模拟BN的forward过程。 running_mean、running_var的更新逻辑。 γ、β参数的更新方式。 BN在eval模式下的行为。 BatchNormNd类包括BatchNorm1d、BatchNorm2d、BatchNorm3d,它们的区别在于检查输入的合法性,BatchNorm1d接受2D或3D的输入,BatchNorm2d接受4D的输入,BatchNorm3d接受5D的输入。 接着,介绍SyncBatchNorm的实现。 BN性能与batch size密切相关。在batch size较小的场景中,如检测任务,内存占用较高,单张显卡难以处理较多,导致BN效果不佳。SyncBatchNorm提供了解决方案,其原理是所有计算设备共享同一组BN参数,从而获得全局统计量。 SyncBatchNorm在torch/nn/modules/batchnorm.py和torch/nn/modules/_functions.py中实现,前者负责输入合法性检查以及参数设置,后者负责单卡统计量计算和进程间通信。 SyncBatchNorm的forward过程。 复习方差计算方式。 单卡计算均值、方差,进行归一化处理。 同步所有卡的数据,得到全局均值mean_all和逆标准差invstd_all,计算全局统计量。 接着,介绍SyncBatchNorm的backward过程。 在backward过程中,需要在BN前后进行进程间通信。这在_functions.SyncBatchNorm中实现。 计算weight、bias的梯度以及γ、β,进一步用于计算梯度。从项目的一个 panic 说起:Go 中 Sync 包的分析应用
在项目开发过程中,遇到一个常见的错误——"fatal error: concurrent map read and map write",这是由于Golang内建的map在并发环境下不安全导致的。解决这个问题的方法并不复杂,就是转向使用sync包提供的并发安全的map。
sync包在Golang 1.9之后被官方支持,其中包含了丰富的同步原语,是并发编程的关键部分。在Golang 1.9之前,解决map并发问题通常会借助sync包中的sync.RWMutex或其他锁机制。Golang作为支持用户态进程的编程语言,对并发编程的处理自然离不开锁,这是一种确保多个Goroutine在同一片内存中协同工作的同步机制。
sync包的源码目录结构清晰,包含Mutex、RWmutex、WaitGroup、Map、Once、Cond、Pool等组件。接下来,我们将逐个分析这些同步原语的用途和使用注意事项,重点讨论在项目中常见的sync.Map。
sync.Map是sync包中的一种高效并发安全的map实现,与内建map相比,它提供了Load、Store、LoadOrStore、Delete和Range等方法,并且具有更高的并发性能。虽然sync.Map没有len方法,但其内部机制使得在并发环境中的操作更加稳健。
通过结合实际项目案例和面试题中的陷阱,本文简要探讨了sync包中Mutex、RWMutex、WaitGroup、Once以及Map的使用技巧和注意事项。在实际编程中,正确使用这些同步原语对于避免并发问题至关重要。