Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the implementation principle of WaitGroup in Golang?

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/01 Report--

This article introduces what is the implementation principle of WaitGroup in Golang. The content is very detailed. Interested friends can use it for reference. I hope it will be helpful to you.

1 preface

WaitGroup is a frequently used concurrency control technology in the process of Golang application development.

WaitGroup, which can be understood as Wait-Goroutine-Group, is waiting for a set of goroutine to finish. For example, if a goroutine needs to wait for several other goroutine to be completed, it can be easily implemented using WaitGroup.

Package mainimport ("fmt"time"sync") func main () {var wg sync.WaitGroup wg.Add (2) / / sets the counter, which is the number of goroutine go func () {/ / Do some work time.Sleep (1*time.Second) fmt.Println ("Goroutine 1 finished!") Wg.Done () / / goroutine subtracts the counter by 1} () go func () {/ / Do some work time.Sleep (2*time.Second) fmt.Println ("Goroutine 2 finished!") After the execution of wg.Done () / / goroutine ends, subtract the counter from 1} () wg.Wait () / / the main goroutine blocking wait counter becomes 0 fmt.Printf ("All Goroutine finished!")}

To put it simply, a counter is maintained internally in wg in the above program:

Before starting goroutine, set the counter through Add (2) to the number of goroutine to be started.

After starting goroutine, block yourself with the Wait () method and wait for the counter to change to 0.

At the end of each goroutine execution, the counter is subtracted by 1 through the Done () method.

When the counter changes to 0, the blocked goroutine is awakened.

In fact, WaitGroup can also implement a set of goroutine waiting for another set of goroutine, which is a bit like acrobatics, very error-tolerant, especially if you do not understand how it is implemented. In fact, the implementation source code of WaitGroup is very simple.

2 basic knowledge 2.1 semaphores

Semaphores are a mechanism provided by the Unix system to protect shared resources and prevent multiple threads from accessing a resource at the same time.

It can be simply understood that the semaphore is a numerical value:

When the semaphore > 0, the resource is available, and the system automatically subtracts the semaphore by 1 when the semaphore is obtained.

When the semaphore = 0, the resource is temporarily unavailable. When the semaphore is obtained, the current thread goes to sleep and is awakened when the semaphore is positive.

Since semaphores are also used in the WaitGroup implementation, a brief introduction is made here.

3 WaitGroup3.1 data structure

Src/sync/waitgroup.go:WaitGroup in the source code package defines its data structure:

Type WaitGroup struct {state1 [3] uint32}

State1 is an array of length 3 that contains state and a semaphore, while state is actually two counters:

Counter: the goroutine counter that has not been executed yet

Waiter count: the number of goroutine waiting for the end of goroutine-group, that is, how many waiters there are

Semaphore: semaphores

Considering whether the bytes are aligned, the positions of the three are different. For simplicity, if the bytes are aligned, the positions of the three in memory are as follows:

WaitGroup provides three interfaces:

Add (delta int): add the delta value to the counter

Wait (): waiter increments by 1 and blocks waiting for semaphore semaphore

Done (): counter decreases by 1, and the corresponding number of semaphores are released according to the waiter value.

The implementation details of these three functions are described below.

3.2 Add (delta int)

Add () does two things, one is to add the delta value to the counter, because the delta can be negative, that is to say, the counter may become 0 or negative, so the second thing is that when the countervalue becomes 0, it releases the same amount of semaphores according to the waiter value and wakes up all the waiting goroutine. If the counter becomes negative, then panic.

The pseudo code of Add () is as follows:

Func (wg * WaitGroup) Add (delta int) {statep, semap: = wg.state () / / get state and semaphore address pointer state: = atomic.AddUint64 (statep, uint64 (delta) 32) / / get countervalue w: = uint32 (state) / / get waitervalue if v

< 0 { //经过累加后counter值变为负值,panic panic("sync: negative WaitGroup counter") } //经过累加后,此时,counter >

= 0 / / if counter is positive, it means there is no need to release semaphores. Exit directly / / if waiter is zero, there are no waiters, and there is no need to release semaphores. Exit if v > 0 directly | w = = 0 {return} / / at this point, counter must be equal to 0, and waiter must be greater than 0 (internal maintenance waiter, there will be no less than 0), / / set counter to 0 first Release the semaphore of the number of waiter * statep = 0 for W! = 0; w runtime_Semrelease-{runtime_Semrelease (semap, false) / / release semaphores, one at a time, to wake up a waitress}} 3.3 Wait ()

The Wait () method also does two things, one is to accumulate waiter, and the other is to block waiting semaphores.

Func (wg * WaitGroup) Wait () {statep Semap: = wg.state () / / get the state and semaphore address pointer for {state: = atomic.LoadUint64 (statep) / / get the state value v: = int32 (state > 32) / / get the countervalue w: = uint32 (state) / / get the counter value if v = 0 {/ / if the countervalue is 0 It means that all goroutine have quit and do not need to wait. Return return} / / use CAS (compare exchange algorithm) to accumulate waiter, which may fail. After failure, retry if atomic.CompareAndSwapUint64 (statep, state, state+1) {runtime_Semacquire (semap) / / after successful accumulation through for loop. Wait for the semaphore to wake up your return}

The CAS algorithm is used here to ensure that waiter can be accumulated correctly when multiple goroutine execute Wait () at the same time.

3.4 Done ()

Done () only does one thing, that is, minus counter by 1, and we know that Add () can accept negative values, so Done actually just calls Add (- 1).

The source code is as follows

Func (wg * WaitGroup) Done () {wg.Add (- 1)}

The execution logic of Done () goes to Add (), and it is actually the last completed goroutine that wakes up the waiters.

4 programming Tips

The Add () operation must precede Wait (), otherwise it will panic

The value set by Add () must be the same as the actual number of goroutine waiting, otherwise it will panic

What is the implementation principle of WaitGroup in Golang is shared here, I hope that the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report