This is a creation in Article, where the information may have evolved or changed.
Sync for Golang 1.9. Waitgroup for analysis, the same as the Golang 1.10, except panic
that it will be changed to throw
something other than the others.
Source code location: sync\waitgroup.go
.
Structural body
type WaitGroup struct { noCopy noCopy // noCopy可以嵌入到结构中,在第一次使用后不可复制,使用go vet作为检测使用 // 位值:高32位是计数器,低32位是goroution等待计数。 // 64位的原子操作需要64位的对齐,但是32位。编译器不能确保它,所以分配了12个byte对齐的8个byte作为状态。 state1 [12]byte // byte=uint8范围:0~255,只取前8个元素。转为2进制:0000 0000,0000 0000... ...0000 0000 sema uint32 // 信号量,用于唤醒goroution}
I do not know whether people like me, whether it is the use of Java Countdownlatch or Golang Waitgroup, will be questioned, can be loaded with multiple threads | co-process wait? After reading the source can answer, can be installed
1111 1111 1111 ... 1111\________32___________/
2^32 a lot of spicy! So you don't have to worry about being blown up in a single case.
Function
The following code has removed the race code that is not related to the core code.
Add
Add or decrease the number of waiting goroutine.
Add the delta, which may be negative, to the Waitgroup counter.
- If the counter becomes 0, all blocked Goroutines will be released.
- If the counter becomes negative, it increases panic.
func (WG *waitgroup) Add (delta int) {//Gets the decimal value of the binary corresponding to the elements in the wg.state1 array statep: = Wg.state ()//high 32 bits is Counter state: = Atomic. AddUint64 (Statep, UInt64 (delta) <<32)//Get counter V: = Int32 (State >> +) W: = UInt32 (state)//counter is negative , reported panic if v < 0 {Panic ("sync:negative waitgroup Counter")}//Add and wait for concurrent calls, reported panic if w! = 0 &&A mp Delta > 0 && v = = Int32 (Delta) {Panic ("Sync:waitgroup Misuse:add called concurrently with Wait")} The counter is added successfully if v > 0 | | W = = 0 {return}//When waiting for counter > 0 o'clock, while Goroutine is set to 0. There can be no simultaneous state mutation at this time://-The increase cannot occur simultaneously with the wait,//-if the counter counter = = 0, the wait counter is no longer incremented if *statep! = states {Panic ("Sync: Waitgroup Misuse:add called concurrently with Wait ")}/Reset waiters count to 0. *statep = 0 for; W! = 0; w--{//is intended as a simple wakeup primitive for simultaneous use. True for Wake queue first Goroutine runtime_semrelease (&wg.sema, False)}}
// unsafe.Pointer其实就是类似C的void *,在golang中是用于各种指针相互转换的桥梁。// uintptr是golang的内置类型,是能存储指针的整型,uintptr的底层类型是int,它和unsafe.Pointer可相互转换。// uintptr和unsafe.Pointer的区别就是:unsafe.Pointer只是单纯的通用指针类型,用于转换不同类型指针,它不可以参与指针运算;// 而uintptr是用于指针运算的,GC 不把 uintptr 当指针,也就是说 uintptr 无法持有对象,uintptr类型的目标会被回收。// state()函数可以获取到wg.state1数组中元素组成的二进制对应的十进制的值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])) }}
Done
Equivalent to add (-1).
func (wg *WaitGroup) Done() { // 计数器减一 wg.Add(-1)}
Wait
Execution is blocked until all the Waitgroup number becomes 0.
func (wg *WaitGroup) Wait() { // 获取到wg.state1数组中元素组成的二进制对应的十进制的值 statep := wg.state() // cas算法 for { state := atomic.LoadUint64(statep) // 高32位是计数器 v := int32(state >> 32) w := uint32(state) // 计数器为0,结束等待 if v == 0 { // Counter is 0, no need to wait. return } // 增加等待goroution计数,对低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 } }}
Precautions for use
- Waitgroup cannot guarantee multiple Goroutine execution order
- Waitgroup cannot specify a fixed number of goroutine