針對Golang 1.9的sync.WaitGroup進行分析,與Golang 1.10基本一樣除了將panic
改為了throw
之外其他的都一樣。
源代碼位置:sync\waitgroup.go
。
成都創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供社旗網(wǎng)站建設、社旗做網(wǎng)站、社旗網(wǎng)站設計、社旗網(wǎng)站制作等企業(yè)網(wǎng)站建設、網(wǎng)頁設計與制作、社旗企業(yè)網(wǎng)站模板建站服務,10余年社旗做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡服務。
type WaitGroup struct {
noCopy noCopy // noCopy可以嵌入到結(jié)構(gòu)中,在第一次使用后不可復制,使用go vet作為檢測使用
// 位值:高32位是計數(shù)器,低32位是goroution等待計數(shù)。
// 64位的原子操作需要64位的對齊,但是32位。編譯器不能確保它,所以分配了12個byte對齊的8個byte作為狀態(tài)。
state1 [12]byte // byte=uint8范圍:0~255,只取前8個元素。轉(zhuǎn)為2進制:0000 0000,0000 0000... ...0000 0000
sema uint32 // 信號量,用于喚醒goroution
}
不知道大家是否和我一樣,不論是使用Java的CountDownLatch還是Golang的WaitGroup,都會疑問,可以裝下多個線程|協(xié)程等待呢?看了源碼后可以回答了,可以裝下
1111 1111 1111 ... 1111
\________32___________/
2^32個辣么多!所以不需要擔心單機情況下會被撐爆了。
以下代碼已經(jīng)去掉了與核心代碼無關(guān)的race代碼。
添加或者減少等待goroutine的數(shù)量。
參數(shù)delta可能是負的,加到WaitGroup計數(shù)器,可能出現(xiàn)如下結(jié)果
func (wg *WaitGroup) Add(delta int) {
// 獲取到wg.state1數(shù)組中元素組成的二進制對應的十進制的值
statep := wg.state()
// 高32位是計數(shù)器
state := atomic.AddUint64(statep, uint64(delta)<<32)
// 獲取計數(shù)器
v := int32(state >> 32)
w := uint32(state)
// 計數(shù)器為負數(shù),報panic
if v < 0 {
panic("sync: negative WaitGroup counter")
}
// 添加與等待并發(fā)調(diào)用,報panic
if w != 0 && delta > 0 && v == int32(delta) {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
// 計數(shù)器添加成功
if v > 0 || w == 0 {
return
}
// 當?shù)却嫈?shù)器> 0時,而goroutine設置為0。
// 此時不可能有同時發(fā)生的狀態(tài)突變:
// - 增加不能與等待同時發(fā)生,
// - 如果計數(shù)器counter == 0,不再增加等待計數(shù)器
if *statep != state {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
// Reset waiters count to 0.
*statep = 0
for ; w != 0; w-- {
// 目的是作為一個簡單的wakeup原語,以供同步使用。true為喚醒排在等待隊列的第一個goroutine
runtime_Semrelease(&wg.sema, false)
}
}
// unsafe.Pointer其實就是類似C的void *,在golang中是用于各種指針相互轉(zhuǎn)換的橋梁。
// uintptr是golang的內(nèi)置類型,是能存儲指針的整型,uintptr的底層類型是int,它和unsafe.Pointer可相互轉(zhuǎn)換。
// uintptr和unsafe.Pointer的區(qū)別就是:unsafe.Pointer只是單純的通用指針類型,用于轉(zhuǎn)換不同類型指針,它不可以參與指針運算;
// 而uintptr是用于指針運算的,GC 不把 uintptr 當指針,也就是說 uintptr 無法持有對象,uintptr類型的目標會被回收。
// state()函數(shù)可以獲取到wg.state1數(shù)組中元素組成的二進制對應的十進制的值
func (wg *WaitGroup) state() *uint64 {
if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {
return (*uint64)(unsafe.Pointer(&wg.state1))
} else {
return (*uint64)(unsafe.Pointer(&wg.state1[4]))
}
}
相當于Add(-1)。
func (wg *WaitGroup) Done() {
// 計數(shù)器減一
wg.Add(-1)
}
執(zhí)行阻塞,直到所有的WaitGroup數(shù)量變成0。
func (wg *WaitGroup) Wait() {
// 獲取到wg.state1數(shù)組中元素組成的二進制對應的十進制的值
statep := wg.state()
// cas算法
for {
state := atomic.LoadUint64(statep)
// 高32位是計數(shù)器
v := int32(state >> 32)
w := uint32(state)
// 計數(shù)器為0,結(jié)束等待
if v == 0 {
// Counter is 0, no need to wait.
return
}
// 增加等待goroution計數(shù),對低32位加1,不需要移位
if atomic.CompareAndSwapUint64(statep, state, state+1) {
// 目的是作為一個簡單的sleep原語,以供同步使用
runtime_Semacquire(&wg.sema)
if *statep != 0 {
panic("sync: WaitGroup is reused before previous Wait has returned")
}
return
}
}
}
本文名稱:GolangWaitGroup源碼分析
分享URL:http://sd-ha.com/article4/ipdjie.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供域名注冊、手機網(wǎng)站建設、移動網(wǎng)站建設、網(wǎng)站內(nèi)鏈、、微信公眾號
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)